Passer au contenu principal
2013-11-15

Spring Data - 20131114

1
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
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
Spring Data - Introduction
⦿Utilité du pattern DAO ?
⦿Hétérogénéité des bases de données

2013-11-15

Spring Data - 20131114

4
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
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
Spring Data

Spring Data JPA

Spring Data
Neo4j

Spring Data
MongoDB

Spring Data
Gemfire

Spring Data Commons

2013-11-15

Spring Data – 20131114

7
Spring Data
⦿Utilisé sur un projet chez SFR
⦿Motivations

2013-11-15

Spring Data - 20131114

8
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
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
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
Spring Data JPA
Manipule

JPA

Spring Data

Se base sur
Hibernate

EclipseLink

…

JDBC

BDD
2013-11-15

Spring Data - 20131114 - sdjpabase

12
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Spring Data JPA
⦿Comment s’opère la magie de Spring Data
(attention, spoiler) ?

2013-11-15

Spring Data – 20131114 - proxylab

59
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Spring Data REST
⦿Limites de Spring Data REST

2013-11-15

Spring Data - 20131114 - sdrestws

91
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
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
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
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
Spring Data

2013-11-15

Spring Data - 20131114

96