Présentation spring data Matthieu Briend

4,477 views
4,269 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,477
On SlideShare
0
From Embeds
0
Number of Embeds
113
Actions
Shares
0
Downloads
112
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Présentation spring data Matthieu Briend

  1. 1. 2013-11-15 Spring Data - 20131114 1
  2. 2. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 2
  3. 3. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 3
  4. 4. Spring Data - Introduction ⦿Utilité du pattern DAO ? ⦿Hétérogénéité des bases de données 2013-11-15 Spring Data - 20131114 4
  5. 5. Spring Data - Introduction ⦿Module Spring ⦿Son but : ⦿Faciliter l’écriture des couches d’accès aux données ⦿Tenter d’offrir une abstraction commune pour l’accès aux données quelques soient les sources de données sous-jacentes, tout en prenant en compte les spécificités de celles-ci ⦿Sources de données : JPA, Neo4j, MongoDB, GemFire, Hadoop, ElasticSearch, REST, Redis, Couchbase, … 2013-11-15 Spring Data - 20131114 5
  6. 6. Spring Data API de manipulation de la source de données (JPA, Neo4j, MongoDB, etc.) Manipule Spring Data Source de données 2013-11-15 Spring Data – 20131114 6
  7. 7. Spring Data Spring Data JPA Spring Data Neo4j Spring Data MongoDB Spring Data Gemfire Spring Data Commons 2013-11-15 Spring Data – 20131114 7
  8. 8. Spring Data ⦿Utilisé sur un projet chez SFR ⦿Motivations 2013-11-15 Spring Data - 20131114 8
  9. 9. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 9
  10. 10. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 10
  11. 11. Spring Data JPA ⦿Spring Data JPA offre une couche d’abstraction supplémentaire par rapport à JPA ⦿Se charge de l'implémentation des fonctionnalités les plus courantes des DAO ⦿On se concentre sur l’essentiel : l’écriture des requêtes 2013-11-15 Spring Data - 20131114 - sdjpabase 11
  12. 12. Spring Data JPA Manipule JPA Spring Data Se base sur Hibernate EclipseLink … JDBC BDD 2013-11-15 Spring Data - 20131114 - sdjpabase 12
  13. 13. Spring Data JPA ⦿Pour l'intégrer, il suffit d'ajouter une dépendance dans Maven : <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.2.0.RELEASE</version> </dependency> 2013-11-15 Spring Data - 20131114 - sdjpabase 13
  14. 14. Spring Data JPA ⦿Principe de base ⦿Pour écrire le DAO pour un type d’entités, il faut étendre certaines interfaces fournies par Spring Data : public interface PersonneDao extends CrudRepository<Personne, Long> { } Nom de l'entité qu'on manipule Type de l'identifiant de l'entité 2013-11-15 Spring Data - 20131114 - sdjpabase 14
  15. 15. Spring Data JPA ⦿Les différentes interfaces : ⦿Repository : vous ne ferez pas grand-chose avec, hormis les méthodes que vous ajouterez ⦿CrudRepository : vous aurez des fonctionnalités CRUD de base ⦿PagingAndSortingRepository : vous aurez en plus des méthodes pour la pagination et le tri ⦿JpaRepository : vous aurez en plus des méthodes propres à JPA 2013-11-15 Spring Data - 20131114 - sdjpabase 15
  16. 16. Spring Data JPA Spring Data Commons Repository CrudRepository PagingAndSortingRepository JpaRepository Pas de méthode save(S), findOne(ID), exists(), findAll(), deleteAll(), … findAll(Sort), findAll(Pageable) flush(), saveAndFlush(T), deleteInBatch(Iterable<T>), … Spring Data JPA 2013-11-15 Spring Data - 20131114 - sdjpabase 16
  17. 17. Spring Data JPA ⦿ CrudRepository : public interface PersonneDao extends CrudRepository<Personne, Long> {} public class PersonneDaoTest { @Autowired private PersonneDao personneDao; public void setup() { personneDao.deleteAll(); } public void testSave() { … Personne personneSauvee = personneDao.save(personne); …} public void testCrudDao() {… Assert.assertEquals(1, personneDao.count()); Assert.assertEquals(true, personneDao.exists(personneSauvee.getId())); for (Personne personneDeLaListe : personneDao.findAll()) {…} Personne personneTrouvee = personneDao.findOne(personneSauvee.getId()); personneDao.delete(personneSauvee); Assert.assertEquals(0, personneDao.count());… } 2013-11-15 Spring Data - 20131114 - sdjpabase 17
  18. 18. Spring Data JPA ⦿ PagingAndSortingRepository : public interface PersonnePaginationDao extends PagingAndSortingRepository<Personne, Long>{} public class PersonnePaginationDaoTest { private PersonnePaginationDao personnePaginationDao; public void testTriDesc(){… Iterable<Personne> personnesTrouvees = personnePaginationDao.findAll(new Sort(Sort.Direction.DESC, "nom")); …} public void testPagination() { Assert.assertEquals(10, personnePaginationDao.count()); Page<Personne> personnes = personnePaginationDao.findAll(new PageRequest(1, 3)); Assert.assertEquals(1, personnes.getNumber()); Assert.assertEquals(3, personnes.getSize()); // la taille de la pagination Assert.assertEquals(10, personnes.getTotalElements()); //nb total d'éléments récupérables Assert.assertEquals(4, personnes.getTotalPages()); // nombre de pages Assert.assertTrue(personnes.hasContent()); …} } 2013-11-15 Spring Data - 20131114 - sdjpabase 18
  19. 19. Spring Data JPA ⦿Vous pouvez filtrer les méthodes que vous voulez être utilisables : il suffit de les copier dans votre interface qui étendra l'interface Spring la plus restrictive ⦿Par exemple, pour n'avoir que les méthodes findOne et save : interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> { T findOne(ID id); T save(T entity); } 2013-11-15 Spring Data - 20131114 - sdjpabase 19
  20. 20. Spring Data JPA ⦿ Vous pouvez aussi écrire une requête juste avec le nom de la méthode ⦿ Exemple : public interface RequetesPersonnaliseesDao extends CrudRepository<Personne, Long> { public Personne findByNom(String nom); public Personne findByNomOrPrenom(String nom, String prenom); public Personne findByVelo(Velo velo); public Personne findByVeloAndNom(Velo velo, String nom); …} List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname); List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname); List<Person> findByLastnameOrderByFirstnameAsc(String lastname); 2013-11-15 Spring Data - 20131114 - sdjpabase 20
  21. 21. Spring Data JPA Logique GreaterThan, IsGreaterThan IN In, IsIn IS Is, Equals, (or no keyword) IS_NOT_NULL NotNull, IsNotNull IS_NULL Null, IsNull LESS_THAN LessThan, IsLessThan LIKE Like, IsLike NOT Not, IsNot NotIn, IsNotIn NOT_LIKE NotLike, IsNotLike REGEX Regex, MatchesRegex, Matches … 2013-11-15 GREATER_THAN NOT_IN ⦿Il existe une série de mots-clés pour écrire sa requête (cf. annexe de la documentation de référence) Mot-clé Spring Data … Spring Data - 20131114 - sdjpabase 21
  22. 22. Spring Data JPA ⦿Spring va créer une requête à partir des propriétés et des mots-clés mentionnés dans le nom de la méthode ⦿Si on fait une recherche à partir d’une « souspropriété », on donne à Spring le chemin vers celle-ci. Exemple : Si Person a une propriété Address qui a une propriété ZipCode, on peut faire : List<Person> findByAddressZipCode(ZipCode zipCode); 2013-11-15 Spring Data - 20131114 - sdjpabase 22
  23. 23. Spring Data JPA ⦿S’il y a ambiguïté dans les propriétés, on peut mettre un « _ ». Exemple : Si la classe Person a un attribut addressZip et un autre address (de type Address qui contient ZipCode) : List<Person> findByAddress_ZipCode(ZipCode zipCode); 2013-11-15 Spring Data - 20131114 - sdjpabase 23
  24. 24. Spring Data JPA ⦿Certains types de Spring sont automatiquement reconnus. Du coup, on peut ajouter des paramètres de pagination et de tri : Page<User> findByLastname(String lastname, Pageable pageable); List<User> findByLastname(String lastname, Sort sort); List<User> findByLastname(String lastname, Pageable pageable); 2013-11-15 Spring Data - 20131114 - sdjpabase 24
  25. 25. Spring Data JPA ⦿Exemple : public interface RequetesPersonnaliseesDao extends CrudRepository<Personne, Long> { public List<Personne> findByNomStartingWith(String nom, Sort ordreTri); } public void testRecuperationParNomEtTri() { String baseNom = "aaa"; // sauvegarde des personnes avec pour nom : "baseNom"+i, avec i={0, 1, 2, 3} final List<Personne> listePersonnes = this.requetesPersonnaliseesDao.findByNomStartingWith(baseNom, new Sort(Direction.DESC, "nom")); for (Personne personne : listePersonnes) { System.out.println(personne.getNom()); } } Affiche : aaa3, aaa2, aaa1, aaa0 2013-11-15 Spring Data - 20131114 - sdjpabase 25
  26. 26. Spring Data JPA ⦿Requêtes nommées : ⦿On peut les mettre dans le META-INF/orm.xml ou en annotations dans l’entité ⦿Exemple : @Entity @NamedQuery(name = "User.findByEmailAddress", query = "select u from User u where u.emailAddress = ?1") public class User { … } ⦿Et dans le répository, on ne fait que déclarer la méthode : public interface UserRepository extends JpaRepository<User, Long> { User findByEmailAddress(String emailAddress); } 2013-11-15 Spring Data - 20131114 - sdjpabase 26
  27. 27. Spring Data JPA ⦿ Vous pouvez aussi ajouter l’annotation @Query si vos noms de méthodes sont beaucoup trop longues ⦿ Exemple : @Query("from Personne p where p.nom = ?1 and p.prenom = ?2") public Personne maRequêteAvecQueryDeRechercheParNomEtPrenom(String nom, String prenom); ⦿ Ca marche aussi avec les requêtes de modification, pour lesquelles il faut l’annotation @Modifying : @Query("update Personne p set p.nom = :nom where p.id = :id") @Modifying public int metAJourNom(@Param("nom")String nom, @Param("id") Long id); ⦿ On peut nommer les paramètres avec @Param 2013-11-15 Spring Data - 20131114 - sdjpabase 27
  28. 28. Spring Data JPA ⦿On peut mettre plusieurs arguments que Spring comprendra en fonction de leur déclaration dans la méthode @Query("from Personne p where p.nom = ?1 and p.prenom = ?2") public Personne maRequêteAvecQueryDeRechercheParNomEtPrenom(String nom, String prenom); ⦿@Query prend l’ascendant sur les requêtes nommées 2013-11-15 Spring Data - 20131114 - sdjpabase 28
  29. 29. Spring Data JPA ⦿Que ce soit pour @Query ou @NamedQuery, on peut mettre du code SQL natif (respectivement avec l’attribut nativeQuery et @NamedNativeQuery) 2013-11-15 Spring Data - 20131114 - sdjpabase 29
  30. 30. Spring Data JPA ⦿3 stratégies pour dire à Spring comment récupérer les requêtes : ⦿CREATE : à partir des noms des méthodes ⦿USE_DECLARED_QUERY : annotations, requêtes nommées ou tout autre moyen propre à la source de données sous-jacente ⦿CREATE_IF_NOT_FOUND : recherche d’abord une requête associée, puis crée la requête à partir du nom de la méthode ⦿Déclaré dans la configuration Spring 2013-11-15 Spring Data - 20131114 - sdjpabase 30
  31. 31. Spring Data JPA ⦿ Comment configurer Spring Data JPA avec Hibernate et H2 : <!-- Déclaration des paquetages contenant les DAO --> <jpa:repositories base-package="fr.soat.springdata.jpa.dao" /> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- <property name="persistenceUnitName" value="spring-jpa" /> utile si on a un persistence.xml --> <property name="packagesToScan" value="fr.soat.springdata.jpa.entites" /> <!-- pour se débarasser du persistence.xml --> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> <property name="database" value="H2" /> </bean> </property> </bean> <jdbc:embedded-database id="dataSource" type="H2" /> 2013-11-15 Spring Data - 20131114 - sdjpabase 31
  32. 32. Spring Data JPA ⦿La balise <repositories /> existe pour chaque module de Spring Data ⦿Spring scanne les paquetages mentionnés à la recherche de Repository ⦿Pour chaque interface trouvée, Spring va créer le FactoryBean qui va construire le proxy qui va traiter les appels ⦿Spring active la récupération des exceptions levées par la source de données et les convertit en DataAccessException 2013-11-15 Spring Data - 20131114 - sdjpabase 32
  33. 33. Spring Data JPA ⦿Les requêtes personnalisées ⦿2 possibilités : ⦿Soit vous voulez écrire une méthode personnalisée pour tous les DAO ⦿Soit vous voulez juste ajouter une méthode personnalisée pour un DAO 2013-11-15 Spring Data - 20131114 - sdjpabase 33
  34. 34. Spring Data JPA ⦿ Ajouter une méthode personnalisée pour tous les DAO ⦿ L’applicationContext.xml devra être modifié : <jpa:repositories base-package="fr.soat.springdata.jpa.dao" factory-class= "fr.soat.springdata.jpa.dao.personnalisees.tous.DaoCommunAuxAutresD aoFactoryBean" /> 2013-11-15 Spring Data - 20131114 - sdjpabase 34
  35. 35. Spring Data JPA ⦿ Ajouter une méthode personnalisée pour tous les DAO ⦿ FactoryBean devra être créé : Un public class DaoCommunAuxAutresDaoFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> { @Override protected RepositoryFactorySupport createRepositoryFactory(final EntityManager entityManager) { // dans JpaRepositoryFactoryBean, on ne fait que renvoyer un JpaRepositoryFactory return new DaoCommunAuxAutresDaoFactory<T, I>(entityManager); } … 2013-11-15 Spring Data - 20131114 - sdjpabase 35
  36. 36. Spring Data JPA ⦿Ajouter une méthode personnalisée pour tous les DAO ⦿Un FactoryBean devra être créé (2) : … private static class DaoCommunAuxAutresDaoFactory<T, I extends Serializable> extends JpaRepositoryFactory { private EntityManager entityManager; public DaoCommunAuxAutresDaoFactory(final EntityManager entityManager) { super(entityManager); this.entityManager = entityManager; } @Override @SuppressWarnings("unchecked") protected Object getTargetRepository(final RepositoryMetadata metadata) { // dans JpaRepositoryFactory, on renvoyait un SimpleJpaRepository return new DaoCommunAuxAutresDaoImpl<T, I>((Class<T>) metadata.getDomainType(), this.entityManager); } @Override protected Class<?> getRepositoryBaseClass(final RepositoryMetadata metadata) { /* On peut ignorer metadata qui sert à JpaRepositoryFactory */ return DaoCommunAuxAutresDao.class; // dans JpaRepositoryFactory, on renvoyait soit un SimpleJpaRepository.class soit un QueryDslJpaRepository.class }}} 2013-11-15 Spring Data - 20131114 - sdjpabase 36
  37. 37. Spring Data JPA ⦿ Ajouter une méthode personnalisée pour tous les DAO ⦿ L’interface qui devra être étendue par tous les autres Repository : @NoRepositoryBean public interface DaoCommunAuxAutresDao<T, ID extends Serializable> extends JpaRepository<T, ID>{ List<T> uneMethodeDeRechercheCommuneATousLesDaoParLExemple(T exemple); } 2013-11-15 Spring Data - 20131114 - sdjpabase 37
  38. 38. Spring Data JPA ⦿Ajouter une méthode personnalisée pour tous les DAO ⦿La classe implémentant l’interface commune à tous les DAO : public class DaoCommunAuxAutresDaoImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements DaoCommunAuxAutresDao<T, ID> { private EntityManager entityManager; public DaoCommunAuxAutresDaoImpl(final Class<T> domainClass, final EntityManager entityManager) { super(domainClass, entityManager); this.entityManager = entityManager; } @Override public List<T> uneMethodeDeRechercheCommuneATousLesDaoParLExemple(final T exemple) { Session session = (Session) this.entityManager.getDelegate(); Example hibernateExample = Example.create(exemple).ignoreCase().enableLike(MatchMode.ANYWHERE); Criteria criteria = session.createCriteria(exemple.getClass()).add(hibernateExample); return criteria.list(); } } 2013-11-15 Spring Data - 20131114 - sdjpabase 38
  39. 39. Spring Data JPA ⦿ Ajouter une méthode personnalisée pour tous les DAO ⦿ Repository quelconque : Un public interface AutrePersonneDao extends DaoCommunAuxAutresDao<Personne, Long>{} 2013-11-15 Spring Data - 20131114 - sdjpabase 39
  40. 40. Spring Data JPA ⦿Ajouter une méthode personnalisée pour tous les DAO ⦿La classe de test : @ContextConfiguration("classpath:applicationContext_daoCommun.xml")… public class PersonneDaoPersonnaliseeBaseImplTest { @Autowired private AutrePersonneDao autrePersonneDao; @Test public void test() { List<Personne> personnesTrouvees = this.autrePersonneDao.uneMethodeDeRechercheCommuneATousLesDaoPa rLExemple(this.personneSauvee); } } 2013-11-15 Spring Data - 20131114 - sdjpabase 40
  41. 41. Spring Data JPA (QueryDsl) ⦿ Parenthèse sur QueryDsl ⦿ QueryDsl est un outil ayant le même but que l'API Criteria (écriture des requêtes avec vérification lors de la compilation), mais qui rend les requêtes plus parlantes ⦿ Criteria : CriteriaQuery query = builder.createQuery(); Root<Person> men = query.from( Person.class ); Root<Person> women = query.from( Person.class ); Predicate menRestriction = builder.and( builder.equal( men.get( Person_.gender ), Gender.MALE ), builder.equal( men.get( Person_.relationshipStatus ),RelationshipStatus.SINGLE )); Predicate womenRestriction = builder.and( builder.equal( women.get( Person_.gender ), Gender.FEMALE ), builder.equal( women.get( Person_.relationshipStatus ),RelationshipStatus.SINGLE )); query.where( builder.and( menRestriction, womenRestriction ) ); ⦿ QueryDsl : JPAQuery query = new JPAQuery(em); QPerson men = new QPerson("men"); QPerson women = new QPerson("women"); query.from(men, women).where( men.gender.eq(Gender.MALE), men.relationshipStatus.eq(RelationshipStatus.SINGLE), women.gender.eq(Gender.FEMALE), women.relationshipStatus.eq(RelationshipStatus.SINGLE)); 2013-11-15 Spring Data - 20131114 - sdjpabase 41
  42. 42. Spring Data JPA ⦿Ajouter une méthode personnalisée à un DAO ⦿Il faut une interface qui va contenir la méthode personnalisée : public interface PersonneDaoAvecMethodePersonnalisee { public List<Personne> uneMethodePersonnaliseeDeRechercheParNom(String nom); } 2013-11-15 Spring Data - 20131114 - sdjpabase 42
  43. 43. Spring Data JPA ⦿Ajouter une méthode personnalisée à un DAO ⦿Il faut ensuite une classe implémentant l’interface (nom = nom de l’interface + "Impl", configurable) : public class PersonneDaoPersonnaliseeRepositoryImpl implements PersonneDaoAvecMethodePersonnalisee { @PersistenceContext private EntityManager entityManager; @Override public List<Personne> uneMethodePersonnaliseeDeRechercheParNom(String nom) { return this.entityManager.createQuery("from Personne p where p.nom = ?1").setParameter(1, nom).getResultList(); } } 2013-11-15 Spring Data - 20131114 - sdjpabase 43
  44. 44. Spring Data JPA ⦿Ajouter une méthode personnalisée à un DAO ⦿On termine en créant l’interface DAO qui sera utilisée et à qui on va adjoindre la méthode personnalisée : public interface PersonneDaoPersonnaliseeRepository extends CrudRepository<Personne, Long>, PersonneDaoAvecMethodePersonnalisee {} 2013-11-15 Spring Data - 20131114 - sdjpabase 44
  45. 45. Spring Data JPA ⦿Ajouter une méthode personnalisée à un DAO ⦿La classe utilisatrice : public class PersonneDaoPersonnaliseeRepositoryTest { @Autowired private PersonneDaoPersonnaliseeRepository personneDaoPersonnaliseeRepository; … @Test public void test() { List<Personne> personneList = this.personneDaoPersonnaliseeRepository.uneMethodePersonnaliseeDeRecherc heParNom(this.personneSauvee.getNom()); } } 2013-11-15 Spring Data - 20131114 - sdjpabase 45
  46. 46. Spring Data JPA ⦿Comment bénéficier de l'avantage de l'API Criteria (vérification des requêtes à la compilation) ? ⦿Utiliser des Specification ⦿Intégrer QueryDsl ⦿Inspiré des concepts du Domain Driven Design ⦿Plus d'informations ici : http://spring.io/blog/2011/04/26/advancedspring-data-jpa-specifications-and-querydsl 2013-11-15 Spring Data - 20131114 - sdjpabase 46
  47. 47. Spring Data JPA ⦿Principe de l'utilisation des Specification ⦿Créer un objet qui implémente l'interface Specification ⦿Le DAO étend en plus , JpaSpecificationExecutor ⦿Mettre le ou une combinaison de Specification en paramètre des méthodes classiques de Spring Data 2013-11-15 Spring Data - 20131114 - sdjpabase 47
  48. 48. Spring Data JPA ⦿ Exemple d'utilisation des Specification 1. On crée des objets Specification : public CustomerSpecifications { public static Specification<Customer> customerHasBirthday() { return new Specification<Customer> { public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) { return cb.equal(root.get(Customer_.birthday), today); } }; } public static Specification<Customer> isLongTermCustomer() { return new Specification<Customer> { public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) { return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2)); } }; } } 2013-11-15 Spring Data - 20131114 - sdjpabase 48
  49. 49. Spring Data JPA ⦿Exemple d'utilisation des Specification 2. Notre DAO étend en plus JpaSpecificationExecutor : public interface CustomerRepository extends JpaRepository<Customer>, JpaSpecificationExecutor {} 3. Le client peut ensuite les utiliser : customerRepository.findAll(hasBirthday()); customerRepository.findAll(isLongTermCustomer()); customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCu stomer())); 2013-11-15 Spring Data - 20131114 - sdjpabase 49
  50. 50. Spring Data JPA ⦿Principe de l'utilisation de QueryDsl ⦿On inclut le plugin Maven de QueryDsl ⦿Le DAO étend en plus QueryDslPredicateExecutor 2013-11-15 Spring Data - 20131114 - sdjpabase 50
  51. 51. Spring Data JPA ⦿Exemple d'utilisation de QueryDsl 1. Notre DAO étend en plus JpaSpecificationExecutor : public interface CustomerRepository extends JpaRepository<Customer>, QueryDslPredicateExecutor { } 2. Le client peut ensuite les utiliser : BooleanExpression customerHasBirthday = customer.birthday.eq(today); BooleanExpression isLongTermCustomer = customer.createdAt.lt(today.minusYears(2)); customerRepository.findAll(customerHasBirthday.and(isLongTermCustomer)); 2013-11-15 Spring Data - 20131114 - sdjpabase 51
  52. 52. Spring Data JPA ⦿Les transactions : ⦿Toutes les méthodes pour récupérer des données sont en readOnly=true. Les autres sont en @Transactional(donc readOnly= false) ⦿@Transactional est configurable (timeout, readOnly, …) ⦿Pour qu’une suite d’opérations soient dans la même transaction, il suffit de mettre @Transactional dans la méthode englobante. Il faudra ajouter <tx:annotation-driven /> dans la configuration 2013-11-15 Spring Data - 20131114 - sdjpabase 52
  53. 53. Spring Data JPA ⦿L’intégration de Spring Data avec Spring MVC ⦿Offre quelques facilités : ⦿Récupération automatique des entités ⦿Pagination 2013-11-15 Spring Data – 20131114 - sdjpawebapp 53
  54. 54. Spring Data JPA ⦿ La récupération automatique des entités ⦿ Avant, on était obligé, à partir de l’identifiant, de récupérer les entités de la base de données : @Controller @RequestMapping("/welcome/") public class HelloController { @RequestMapping("/voir/{id}") public String voirVelo(@PathVariable("id") final Long id, final Model modele) { Velo veloTrouve = this.veloDao.findOne(id); modele.addAttribute("message", "Modèle de vélo : " + veloTrouve.getModele()); return "basique/fiche"; } } ⦿ C’est très fastidieux 2013-11-15 Spring Data - 20131114 - sdjpawebapp 54
  55. 55. Spring Data JPA ⦿ Avec Spring Data, si on met directement en paramètre une entité, celle-ci sera chargée automatiquement : @Controller @RequestMapping("/sdtouch/") public class ControleurUnPeuMieuxAvecSD { @RequestMapping("/voir/{id}") public String voirVelo(@PathVariable("id") final Velo veloTrouve, final Model modele) { modele.addAttribute("message", "Modèle de vélo : " + veloTrouve.getModele()); return "sdtouch/fiche"; } } 2013-11-15 Spring Data - 20131114 - sdjpawebapp 55
  56. 56. Spring Data JPA ⦿ Pour cela, il faut modifier un peu le dispatcherServletservlet.xml pour y ajouter un convertisseur : <mvc:annotation-driven conversion-service="conversionService" /> <!-- Nécessaire pour la conversion String (id) vers une entité récupéré par Spring Data (avec findOne()) --> <bean class= "org.springframework.data.repository.support.DomainClassConverter"> <!-- on va ajouter DomainClassConverter à la liste des converters de conversionService --> <constructor-arg ref="conversionService" /> </bean> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean" /> 2013-11-15 Spring Data - 20131114 - sdjpawebapp 56
  57. 57. Spring Data JPA ⦿Pour la pagination, il faut récupérer les paramètres de la requête HTTP pour récupérer la page à afficher et la taille de la page : @RequestMapping(value="/{page}/{pageSize}", method = RequestMethod.GET) public String printWelcome(final Model modele, @PathVariable("page") final int page, @PathVariable("pageSize") final int pageSize) { Pageable pageable = new PageRequest(page, pageSize); Page<Velo> resultat = this.veloDao.findAll(pageable); return "basique/hello"; } 2013-11-15 Spring Data - 20131114 - sdjpawebapp 57
  58. 58. Spring Data JPA ⦿Spring Data améliore un peu la chose : @RequestMapping(method = RequestMethod.GET) public String printWelcome(final Model modele, @PageableDefaults(pageNumber = 0, value = 2)final Pageable pageable) { final Page<Velo> resultatRecherche = this.veloDao.findAll(pageable); modele.addAttribute("velosTrouves", resultatRecherche.getContent()); modele.addAttribute("pagination", resultatRecherche); return "sdtouch/hello"; } 2013-11-15 Spring Data - 20131114 - sdjpawebapp 58
  59. 59. Spring Data JPA ⦿Comment s’opère la magie de Spring Data (attention, spoiler) ? 2013-11-15 Spring Data – 20131114 - proxylab 59
  60. 60. Spring Data JPA ⦿Comment s’opère la magie de Spring Data ? ⦿Chargement des requêtes au chargement : ⦿Scanne les paquetages mentionnés dans l’applicationContext.xml ⦿Quand Spring trouve un repository, JpaRepositoryFactory va créer un SimpleJpaRepository qui implémente JpaRepository ⦿Il met ensuite cet objet dans un Proxy ⦿Juste avant, il va associer à ce proxy un intercepteur de méthode qui permet la résolution des requêtes personnalisées, nommées, avec @Query, etc. Donc à ce stade, les requêtes sont résolues et en cache ⦿Appel des méthodes des interfaces ⦿Quand une de ces méthodes est appelée, c’est en réalité le proxy qui est appelé ⦿Celui-ci va appeler la méthode correspondante de l’objet SimpleJpaRepository qu’on lui a donné au chargement 2013-11-15 Spring Data - 20131114 - proxylab 60
  61. 61. Spring Data JPA ⦿Comment s’opère la magie de Spring Data ? Exemple. ⦿"Spring" : public void injecteDansClient(final Client client) { UnDao dao = (UnDao) Proxy.newProxyInstance(Thread.currentThread(). getContextClassLoader(), new Class[] { UnDao.class }, new ProxyInjecte()); client.setDao(dao); } 2013-11-15 Spring Data - 20131114 - proxylab 61
  62. 62. Spring Data JPA ⦿Comment s’opère la magie de Spring Data ? Exemple. ⦿L'InvocationHandler : class ProxyInjecte implements InvocationHandler { private ClasseNonAppeleeParClient classeNonAppeleeParClient = new ClasseNonAppeleeParClient(); public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { if (method.getName().equals("bambiFaitDodo")) { this.classeNonAppeleeParClient.bambiFaitDodo(); } else if (method.getName().equals("atchoum")) { this.classeNonAppeleeParClient.atchoum(); } return null; } } 2013-11-15 Spring Data - 20131114 - proxylab 62
  63. 63. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 63
  64. 64. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 64
  65. 65. Spring Data Neo4j ⦿Neo4j : une base de données orientée graphe ⦿Cas d'utilisation typique : les réseaux sociaux ⦿Spring Data Neo4j apporte un niveau d’abstraction supplémentaire à la manipulation des graphes 2013-11-15 Spring Data - 20131114 65
  66. 66. Spring Data Neo4j ⦿ Neo4j quelques commandes de base ⦿ Démarrage d’une base Neo4j en mémoire : public class Neo4jMain { private final static String DB_PATH = "bdd_neo4j"; public static void main(String[] args) { GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH); registerShutdownHook(graphDb); } private static void registerShutdownHook(final GraphDatabaseService graphDb) { /* Pour que Neo4j s'arrête correctement, même en cas de Ctrl-C */ Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { graphDb.shutdown(); } } ); }} 2013-11-15 Spring Data – 20131114 - neo4jbasique 66
  67. 67. Spring Data Neo4j ⦿ Neo4j quelques commandes de base ⦿ Pour créer le graphe, qu’avec des noeuds : Transaction tx = this.graphDb.beginTx(); try { this.acteur = this.graphDb.createNode(); this.acteur.setProperty("nom", "Diesel"); this.acteur.setProperty("prenom", "Vin"); Node noeudFilm = this.graphDb.createNode(); noeudFilm.setProperty(Film.TITRE, "Rapides et dangereux"); this.acteur.createRelationshipTo(noeudFilm, JOUE_DANS); noeudFilm = this.graphDb.createNode();// on utilise les noeuds noeudFilm.setProperty(Film.TITRE, "Trop rapide et trop pas content"); this.acteur.createRelationshipTo(noeudFilm, JOUE_DANS); tx.success(); } finally { tx.finish(); } 2013-11-15 Spring Data – 20131114 - neo4jbasique 67
  68. 68. Spring Data Neo4j ⦿Neo4j quelques commandes de base ⦿Pour créer le graphe, en essayant d’utiliser des beans : Transaction tx = this.graphDb.beginTx(); try {… Film film = new Film(this.graphDb.createNode()); film.setTitre("Il faut sauver le soldat Ryan"); this.acteur.createRelationshipTo(film.getNoeud(), JOUE_DANS); tx.success(); } finally { tx.finish(); } 2013-11-15 Spring Data – 20131114 - neo4jbasique 68
  69. 69. Spring Data Neo4j ⦿ Neo4j quelques commandes de base ⦿ Et dans le bean Film, on a : public class Film { public static final String TITRE = "TITRE"; private Node noeud; public Film(Node noeud) { this.noeud = noeud; } public Node getNoeud() { return this.noeud; } public String getTitre() { return (String) this.noeud.getProperty(TITRE); } public void setTitre(String nom) { this.noeud.setProperty(TITRE, nom); } } ⦿ La relation doit étendre RelationshipType : public enum JoueDansRelationEnum implements RelationshipType { JOUE_DANS; } 2013-11-15 Spring Data – 20131114 - neo4jbasique 69
  70. 70. Spring Data Neo4j ⦿ Neo4j quelques commandes de base ⦿ Pour afficher le contenu du graphe, on peut utiliser un « Traverser » : public void afficheGraphe() { Traverser traverseurDeFilms = getTraverseursFilms(); for (Path path : traverseurDeFilms) { if (path.length() == 0) { System.out.println(path.endNode().getProperty("nom") + " a joué dans les films suivants :"); } else { System.out.println(path.endNode().getProperty(Film.TITRE)); } }} private Traverser getTraverseursFilms() { TraversalDescription td = Traversal.description(). breadthFirst(). relationships(JOUE_DANS). evaluator(Evaluators.all()); return td.traverse(this.acteur); } 2013-11-15 Spring Data – 20131114 - neo4jbasique 70
  71. 71. Spring Data Neo4j ⦿ Neo4j quelques commandes de base ⦿ Neo4j a son langage de « requêtage », le Cypher : public void exempleCypher() { String requete = "start n=node("+this.acteur.getId()+") " + "match n-[:JOUE_DANS]->films " + "where films." + Film.TITRE + " =~ 'Trop.*' " + "return n, n.nom, n.prenom, films." + Film.TITRE; ExecutionResult result = this.engine.execute(requete); String rows = ""; for ( Map<String, Object> row : result) { for ( Entry<String, Object> column : row.entrySet() ) { rows += column.getKey() + ": " + column.getValue() + "; "; } rows += "n"; } System.out.println(rows); } ⦿ Affiche : n: Node[13]; n.nom: Diesel; films.TITRE: Trop rapide et trop pas content; n.prenom: Vin; 2013-11-15 Spring Data – 20131114 - neo4jbasique 71
  72. 72. Spring Data Neo4j ⦿Ce qu’apporte Spring Data Neo4j ⦿Les nœuds deviennent des beans Java classiques. Pareil pour les relations ⦿Les opérations de base peuvent se faire à partir des interfaces ⦿Une bonne partie des opérations valables pour JPA sont valables pour Neo4j 2013-11-15 Spring Data – 20131114 - sdneo4jbase 72
  73. 73. Spring Data Neo4j ⦿S'inclut avec une dépendance Maven : <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-neo4j</artifactId> <version>2.3.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-neo4j-aspects</artifactId> <version>2.3.1.RELEASE</version> </dependency> 2013-11-15 Spring Data – 20131114 - sdneo4jbase 73
  74. 74. Spring Data Neo4j ⦿Un exemple de nœud Spring Data Neo4j : @NodeEntity public class Acteur { @GraphId private Long idNoeud; @Indexed(indexType = IndexType.FULLTEXT, indexName="nom") private String nom; private String prenom; public void aJoueDans(Film film) { this.films.add(film); } @RelatedTo(direction=Direction. BOTH, type="aJoueDans") private Set<Film> films; @RelatedToVia private Set<Realisation> realisations; // getters et setters classiques 2013-11-15 public Realisation aRealise(Film film) { Realisation realisation = new Realisation(); realisation.setActeur(this); realisation.setFilm(film); realisations.add(realisation); return realisation; } Spring Data – 20131114 - sdneo4jbase 74
  75. 75. Spring Data Neo4j ⦿Les relations deviennent aussi des beans Java classiques : @RelationshipEntity(type="aRealise") public class Realisation { @GraphId private Long id; private Calendar dateRealisation; @StartNode private Acteur acteur; @EndNode private Film film; // getters et setters classiques 2013-11-15 Spring Data – 20131114 - sdneo4jbase 75
  76. 76. Spring Data Neo4j ⦿Les DAO sont toujours aussi minimalistes : public interface ActeurDao extends GraphRepository<Acteur> { @Query("start acteur=node({0}) " + "match acteur-[:aRealise]->film " + "return film") Iterable<Film> recupereMoiTousLesFilmsRealisesPar(Acteur acteur); } 2013-11-15 Spring Data – 20131114 - sdneo4jbase 76
  77. 77. Spring Data Neo4j ⦿L'utilisation s'en trouve simplifiée : public void setup() { this.acteurDao.deleteAll(); Acteur vinDiesel = new Acteur(); vinDiesel.setNom("Diesel"); vinDiesel.setPrenom("Vin"); vinDiesel.aJoueDans(leFilm("Strays")); vinDiesel.aJoueDans(leFilm("Il faut sauver le soldat Ryan")); vinDiesel.aJoueDans(leFilm("Les Initiés")); final Film multiFacial = leFilm("Multi-Facial"); this.filmDao.save(multiFacial); vinDiesel.aRealise(multiFacial).en(1994); this.acteurDao.save(vinDiesel); } 2013-11-15 Spring Data – 20131114 - sdneo4jbase 77
  78. 78. Spring Data Neo4j ⦿D'autres exemples d'utilisation : EndResult<Film> filmsTrouves = this.filmDao.findAll(); EndResult<Film> filmsTrouves = this.filmDao.findAllByPropertyValue("titre", "Strays"); Film filmTrouve = this.filmDao.findByPropertyValue("titre", "Les Initiés"); Iterable<Film> filmsTrouves = this.filmDao.findByTitreContaining("Il"); Acteur vinDiesel = this.acteurDao.findOne(identifiantVinDiesel); final Iterable<Film> filmsTrouves = this.acteurDao.recupereMoiTousLesFilmsRealisesPar(vinDiesel); 2013-11-15 Spring Data – 20131114 - sdneo4jbase 78
  79. 79. Spring Data Neo4j ⦿On peut aussi utiliser les Traversers : final Acteur vinDiesel = this.acteurDao.findOne(this.identifiantVinDiesel); TraversalDescription traversalDescription = Traversal.description(). breadthFirst(). evaluator(Evaluators.atDepth(1)); final Iterable<Film> filmsJouesParVinDiesel = this.filmDao.findAllByTraversal(vinDiesel, traversalDescription); 2013-11-15 Spring Data – 20131114 - sdneo4jbase 79
  80. 80. Spring Data Neo4j ⦿L'applicationContext.xml : <neo4j:repositories base-package="fr.soat.springdata.neo4j.sdneo4jbase.dao" /> <neo4j:config storeDirectory="data/bdd_neo4j" /> 2013-11-15 Spring Data – 20131114 - sdneo4jbase 80
  81. 81. Spring Data Neo4j ⦿La hiérarchie des interfaces Spring Data : findAllByTraver TraversalRepository sal PagingAndSortingRepository CRUDRepository save(U), findOne(Long), findAll(),… IndexRepository findAllByQuery findAllByRange … GraphRepository 2013-11-15 Spring Data – 20131114 - sdneo4jbase 81
  82. 82. Spring Data JPA Spring Data Commons Repository CrudRepository PagingAndSortingRepository JpaRepository Pas de méthode save(S), findOne(ID), exists(), findAll(), deleteAll(), … findAll(Sort), findAll(Pageable) flush(), saveAndFlush(T), deleteInBatch(Iterable<T>), … Spring Data JPA 2013-11-15 Spring Data - 20131114 - sdjpabase 82
  83. 83. Spring Data Neo4j ⦿D'autres interfaces sont disponibles, comme CypherDslRepository pour exécuter du CypherDsl, SpatialRepository pour les requêtes spatiales 2013-11-15 Spring Data – 20131114 - sdneo4jbase 83
  84. 84. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 84
  85. 85. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 85
  86. 86. Spring Data REST ⦿Spring Data REST simplifie l’exposition des services REST ⦿Pour offrir un service REST, il suffit : ⦿De dire qu’on utilise Spring Data REST pour une source de données particulière (JPA, Neo4j, MongoDB, etc.) ⦿D’étendre une des interfaces de Spring Data ⦿De dire où se situent ces interfaces d’export ⦿De… non, c’est tout 2013-11-15 Spring Data – 20131114 - sdrestws 86
  87. 87. Spring Data REST Spring Data REST WebMVC Spring MVC Spring Data JPA Spring Data Neo4j Spring Data MongoDB Spring Data Commons 2013-11-15 Spring Data – 20131114 - sdjpabase 87
  88. 88. Spring Data REST ⦿ Dans le pom.xml : <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-rest-webmvc</artifactId> <version>1.0.0.RELEASE</version> </dependency> … et les dépendances vers H2 et Hibernate 2013-11-15 Spring Data - 20131114 - sdrestws 88
  89. 89. Spring Data REST ⦿Un brin de folie, le DAO a une annotation à lui : @RestResource(path="chanson") //pour personnaliser un peu la façon dont le service web est appelé public interface ChansonDao extends CrudRepository<Chanson, Long> { } ⦿Le bean Chanson : @Entity public class Chanson { @Id private Long id; private String titre;…} 2013-11-15 Spring Data - 20131114 - sdrestws 89
  90. 90. Spring Data REST ⦿Utilisation : mvn clean install jetty:run lance Jetty et le fait écouter sur le port 8080 curl -H "Content-Type: application/json" -d '{"titre":"Get Lucky","id":"2"}' http://localhost:8080/chanson curl -v http://localhost:8080/chanson/2 Renvoie : { "links" : [ { "rel" : "self"; "href" : "http://localhost:8080/chanson/2" } ], "titre" : "Get Lucky" } 2013-11-15 Spring Data - 20131114 - sdrestws 90
  91. 91. Spring Data REST ⦿Limites de Spring Data REST 2013-11-15 Spring Data - 20131114 - sdrestws 91
  92. 92. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 92
  93. 93. Spring Data ⦿Introduction ⦿Spring Data JPA ⦿Spring Data Neo4j ⦿Spring Data Rest ⦿Aperçu de Spring Data MongoDB 2013-11-15 Spring Data - 20131114 93
  94. 94. Spring Data MongoDb ⦿ Aperçu de Spring Data MongoDB ⦿ On a un bean : @Document(collection = "menu") // annotations pas nécessaires public class MenuItem { @Id private String id; @Field("itemName") @Indexed private String name; } ⦿ Le répository associé public interface MenuItemRepository extends CrudRepository<MenuItem, String> { public List<MenuItem> findByIngredientsNameIn(String... name); } ⦿ Un exemple d'utilisation menuItemRepository.save(eggFriedRice()); List<MenuItem> peanutItems menuItemRepository.findByIngredientsNameIn("Peanuts"); 2013-11-15 Spring Data - 20131114 94
  95. 95. Spring Data - conclusion ⦿Pour aller plus loin : ⦿Le site de Spring Data : http://projects.spring.io/spring-data/ ⦿Le livre sur Spring Data : ⦿http://shop.oreilly.com/product/0636920024767.do ⦿Et ses exemples : https://github.com/springprojects/spring-data-book ⦿Le livre sur Spring Data Neo4j : Good Relationships The Spring Data Neo4j Guide Book ⦿Mes exemples : ⦿https://github.com/xeter/soirees3t 2013-11-15 Spring Data - 20131114 95
  96. 96. Spring Data 2013-11-15 Spring Data - 20131114 96

×