Workshop spring session 2 - La persistance au sein des applications Java
1. Workshop Spring - Session 2
La persistance au sein des
applications Java
Conçu en décembre 2011
Réactualisé en novembre 2014
# 1
2. Agenda du workshop n°2
1. La persistance des données facilitée
Gestion des exceptions
Déclarer une source de données
Classes de support pour JDBC, Hibernate et JPA
2. Encore plus loin avec
Spring Data JPA
et QueryDSL
3. Gestion des transactions
# 2
3. Mise en oeuvre de la persistance
Les atouts du framework Spring :
Facilite l’utilisation de JDBC et des principaux ORM
Rend le code plus lisible
Limite les risques d’erreur inhérents à la libération des ressources
Gestion des exceptions
Laisse la possibilité d’accéder à la technologie sous-jacente
Gère élégamment les transactions
Le projet Spring Data apporte un support :
Encore plus avancé pour JDBC et JPA
Pour les différents types de bases de données
(orientées graphes, colonnes, map …)
# 3
4. # 4
Gestion des exceptions
Les exceptions levées par un DAO doivent être indépendantes de la
technologie sous-jacente :
SQLException pour JDBC
HibernateException pour Hibernate
Spring permet de s’abstraire de ces exceptions techniques en disposant
de sa propre hiérarchie d’exceptions d’accès aux données
Non vérifiée (runtime), DataAccessException en est l’exception parent
Les classes de support d’accès aux données de Spring permettent
d’encapsuler l’exception technique dans une DataAccessException
SQLException avec code erreur ORA-1722 en
DataIntegrityViolationException
5. # 5
Quelques exceptions
DataIntegrityViolationException : utilisée lorsqu’une violation de contrainte
d'intégrité est détectée par la base (ex : contrainte d’unicité, clé étrangère)
DataAccessResourceFailureException : problème de connexion à la base
de données
ConcurrencyFailureException : fait suite à un problème de concurrence
d’accès aux données (verrou pessimiste ou optimiste)
DeadlockLoserDataAccessException : signale que le processus est bloqué
par un verrou
DataRetrievalFailureException : erreur produite lors d’une requête de
sélection (ex: l’objet n’existe pas ou plus)
6. # 6
Source de données
Possibilité Cas d’utilisation Exemple de déclaration
Pool de
connexions
par JNDI
• Application web
déployée dans
un serveur
d’application
<jee:jndi-lookup
id="dataSource"
jndi-name="jdbc/DemoDataSource"/>
Pool de
connexions
embarqué
• Application
standalone
<bean id="dataSource"
class="org.apache.commons.dbcp
.BasicDataSource"
destroy-method="close"
p:driverClassName="${db.driver}"
p:username="{db.user}" ... />
Connexion
unique à la
base
• Tests unitaires
• Batchs
<bean id="dataSource"
class="org.springframework.jdbc
.datasource.SingleConnectionDataSource"
p:suppress-close="true"
p:driverClassName="${db.driver}"
p:username="{db.user}" ... />
8. # 8
Template Method Pattern
Squelette d’un algorithme à l’aide d’opérations abstraites
Permet de masquer le code technique générique
Le développeur se concentre sur la requête et l’extraction du résultat
SQL pour JDC, HQL ou Criteria pour Hibernate …
JdbcOperations DAO StatementCallback
result
execute (callback)
result
doInStatement (statement)
Exécute la requête
Récupère le résultat
Récupère la connexion
Prépare le statement
Gère les exceptions
Ferme le statement
Libère la connexion
9. # 9
Support de JDBC
Les classes JdbcTemplate et NamedParameterJdbcTemplate
centralisent toute la puissance du support JDBC de Spring.
JdbcTemplate template = new JdbcTemplate(dataSource);
String sql = "select count(*) from client";
int nombreClient = template.queryForInt(sql);
sql = "select * from client";
RowMapper<Client> rowMapper =
ParameterizedBeanPropertyRowMapper.newInstance(Client.class);
List<Client> clients = template.query(sql, rowMapper);
sql = "insert into client (id, nom, prenom) VALUES (?, ?, ?)";
template.update(sql, client.getId(), client.getNom(),
client.getPrenom());
Remarque: la classe SimpleJdbcTemplate est dépréciée depuis Spring 3 au profit
des 2 classes citées.
10. # 10
Support des ORM
Solutions ORM supportées par Spring 4 :
JPA 2.1
Hibernate 3.6 et 4.x
JDO 2.x
Cohérence des classes de support de ces différentes technologies
Création et configuration de l’EntityManagerFactory / SessionFactory
Gestion du cycle de vie des EntityManager / sessions Hibernate
Synchronise l’ouverture d’une transaction et la création d’une session
11. # 11
Parallèles entre solutions
Concept JDBC Hibernate JPA
Resource Connection Session EntityManager
Resource factory DataSource SessionFactory EntityManagerFactory
Exception SQLException HibernateException PersistenceException
Classe de
support
JDBC Hibernate JPA
Template class JdbcTemplate HibernateTemplate JpaTemplate
DAO support JdbcDaoSupport HibernateDaoSupport JpaDaoSupport
Transaction DataSourceTransaction
Manager
HibernateTransaction
Manager
JpaTransactionManager
Classes principales des différentes API d’accès aux données
Classes de support de Spring pour les différentes API d’accès aux données
Légende : à éviter pour les nouveaux projets. Préférez l’API native d’Hibernate
supprimer de Spring 4.x
12. Exemple valide en Spring 3.x mais
pas en 4.X suite à la suppression
des 2 classes de support
# 12
Exemples de DAO
@Repository
public class JpaClientDAO extends JpaDaoSupport implements IClientDAO {
public List<Client> findByNom1(String nom) {
return getJpaTemplate().findByNamedQuery("Client.findByNom", nom);
}
public List<Client> findByNom2(String nom) {
String query = “select c from Client c where c.nom = ?1";
return getJpaTemplate().find(query, nom);
}
…
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
@Repository
public class JpaClientDAO implements ICompteDAO {
@PersistenceContext
private EntityManager entityManager;
public List<Client> findAll() {
return entityManager.createQuery("from Client").getResultList();
}
…
Requête nommée définie
sur l’entité Client par
l’annotation @NamedQuery
Requête dynamique JPQL
Injection par Spring du contexte persistant JPA
13. # 13
Encore plus loin
Le projet Spring Data JPA simplifie l’écriture de la couche d’accès aux données
Continuation du projet Hades
Apporte le support pour une approche Domain Driven Developpment (DDD)
Retour à la vraie conception objet
Tout en conservant la séparation entre le fonctionnel et la technique
Utilise le pattern Repository
Manipule uniquement des objets du domaine métier (entités)
Comparé à une interface d’accès à une collection d’objets du domaine
Ne nécessite aucune implémentation : le code est généré par le framework
Fonctions CRUD disponibles en standard : save, findById, count, exists …
14. # 14
Toujours plus loin
Implémentation dynamique des requêtes à partir du nom de la méthode
S’appuie sur des conventions de nommage
Interprète les mots clés : And, Or, Like, OrderBy, NotIn, Between …
Ajout possible de requêtes personnalisées avec leur implémentation
Gestion de la pagination et du tri
Suivi de la création et de la modification des entités (dates et utilisateurs)
Les Spécifications permettent de réutiliser des Criterias JPA 2
Support de la librairie QueryDSL pour requêter des entités
de manière typesafe
avec une syntaxe épurée et fluide
15. # 15
Exemple de JpaRepository
// Repository de l’entité Client dont l’identifiant est un entier
public interface ClientRepository extends JpaRepository<Client, Integer> {
// Recherche par nom de clients
List<Client> findByNom(String nom);
// Recherche effectuée à partir du solde es comptes des clients
List<Client> findByComptesSoldeGreaterThan(Double montant);
// Recherche paginée et ordonnée
Page<Client> findByPrenom(String prenom, Pageable pageable);
// Utilisation de la requête JPQL spécifiée
@Query("select count(c) from Client c where u.prenom=?1")
Long countWithPrenom(String prenom);
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" … >
<!– Détecte et active tous les Repository du package spécifié -->
<jpa:repositories base-package="fr.sqli.bfa.demo.repository" />
</beans>
16. # 16
Query DSL en Action
// Interface héritant de JpaRepository et QueryDslPredicateExecutor
@Autowired
QueryDslJpaRepository<User, Integer> repository;
...
// Méta-modèle de l'entité Client généré à partir des annotations JPA
// Référence les propriétés et les associations d’un client
QClient client = QClient.client;
// Création de prédicats réutilisables
BooleanExpression majeur = client.age.gt(18);
BooleanExpression sousTutelle = client.tuteur.isNotNull();
// Exécution de requêtes type-safe
// - utilisant des prédicats
List<Client> sousResponsabilites = repository.findAll(majeur.or(sousTutelle));
Integer nombreParisiens = repository.count(client.ville.like("paris"));
// - écrites "naturellement" avec Query DSL
JPAQuery query = new JPAQuery(em);
List<Client> clients =
query.from(client).join(client.addresses).fetch().list(client);
Integer maxSolde = query.from(client).uniqueResult(client.solde.max());
17. # 17
Support des transactions
API générique pour la gestion des transactions
Transparence sur la nature d’une transaction (locale ou globale)
Intégration aux différentes technologies :
JDBC, Hibernate, JPA, JMS, JCA, XA
Différentes approches pour la démarcation des transactions
Par programmation
En AOP
Par annotations
Permet d’utiliser le service transactionnel des serveurs JEE
18. # 18
Les attributs transactionnels
Propagation des transactions
Niveau d’isolation
Conditions de rollback
Optimisations sur les transactions en lecture-seule
Time-out
Gestionnaire de transaction
19. # 19
Exemples de propagation
MANDATORY : la méthode doit forcément être exécutée au sein d’un
contexte transactionnel. Si tel n’est pas le cas, une exception est levée.
REQUIRED : la méthode doit forcément être exécutée dans un
contexte transactionnel. S’il n’existe pas lors de l’appel, il est créé.
PROPAGATION_REQUIRES_NEW
Client Service DAO
sans TX
TX1
TX1
TX2
20. # 20
Démarcation par annotations
L’annotation @Transactional permet de déclarer des transactions
Sur une classe ou une interface
l’interface est à privilégier
toutes les méthodes en bénéficient
Sur une méthode
modifie le comportement défini au niveau de la classe
L’annotation @Transactional s’hérite, ce qui permet de :
Bénéficier du comportement transactionnel d’une classe parent
Modifier le comportement d’une classe ou d’une méthode redéfinie
Par défaut, Spring crée un proxy transactionnel à partir de l’interface
Attention aux appels directs de méthode sans passer par le bean
21. # 21
@Transactional par l’exemple
<!-- Active la détection des annotations @Transactional -->
<tx:annotation-driven />
@Transactional(rollbackFor=BusinessException.class)
public interface IBanqueService {
void effectuerVirement(Compte compteSrc, Compte compteDest,
BigDecimal montant) throws SommeIndisponibleException;
@Transactional(readOnly=true)
List<Client> rechercherClient(Criteres criteres);
@Transactional(timeout=30000, Propagation=Propagation.REQUIRES_NEW)
void crediter(Compte compte, BigDecimal montant);
}
22. # 22
Annotation versus AOP
Tableau comparant l’utilisation de l’annotation @Transactional et de
fichier XML pour définir des aspects
Annotation AOP
Avantages + Compilation
+ Productivité
+ Indépendance envers Spring
+ Configuration centralisée
+ Tissage statique possible
Inconvénients - Couplage fort
- Pollution du code
- Lisibilité
- Validation à l’exécution
- Refactoring délicat
- Certaine rigueur nécessaire