Post on 08-Jul-2015
Workshop Spring - Session 2La persistance au sein des
applications Java
Conçu en décembre 2011Réactualisé en novembre 2014
# 1
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
Agenda du workshop n°2
# 2
Mise en œuvre 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
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
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
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 connexionsembarqué
• Application standalone
<bean id="dataSource"
class="org.apache.commons.dbcp
.BasicDataSource"
destroy-method="close"
p:driverClassName="${db.driver}"
p:username="{db.user}" ... />
Connexionunique à 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}" ... />
# 7
Hiérarchie d’exceptionsDataAccessException
NonTransientDataAccessException RecoverableDataAccessException TransientDataAccessException
ConcurrencyFailureException TransientDataAccessResourceExceptionDataIntegrityViolationException PermissionDeniedDataAccessException
OptimisticLockingFailureExceptionPessimisticLockingFailureException
ObjectOptimisticLockingFailureExceptionCannotAcquireLockException
DuplicateKeyException DataRetrievalFailureException
IncorrectResultSizeDataAccessExceptionObjectRetrievalFailureException
# 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 StatementCallbackDAO
result
result
execute (callback)
doInStatement (statement)
Exécute la requêteRécupère le résultat
Récupère la connexionPrépare le statement
Gère les exceptionsFerme le statementLibère la connexion
# 9
Support de JDBC
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());
Les classes JdbcTemplate et NamedParameterJdbcTemplate
centralisent toute la puissance du support JDBC de Spring.
Remarque: la classe SimpleJdbcTemplate est dépréciée depuis Spring 3 au profitdes 2 classes citées.
# 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
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 DataSourceTransactionManager
HibernateTransactionManager
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’Hibernatesupprimer de Spring 4.x
# 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 parl’annotation @NamedQuery
Requête dynamique JPQL
Injection par Spring du contexte persistant JPA
Exemple valide en Spring 3.x maispas en 4.X suite à la suppression des 2 classes de support
# 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
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
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
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
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
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
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
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
@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
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