Bienvenue dans le tutoriel de l’exemple Spring ORM. Aujourd’hui, nous allons examiner un exemple de Spring ORM utilisant la gestion des transactions Hibernate JPA. Je vous montrerai un exemple très simple d’application autonome Spring avec les fonctionnalités suivantes.
- Injection de dépendances (annotation @Autowired)
- EntityManager JPA (fourni par Hibernate)
- Méthodes transactionnelles annotées (annotation @Transactional)
Exemple Spring ORM
J’ai utilisé une base de données en mémoire pour l’exemple Spring ORM, donc aucun besoin de configuration de base de données (mais vous pouvez le changer pour toute autre base de données dans la section datasource du fichier spring.xml). Il s’agit d’une application autonome Spring ORM pour minimiser toutes les dépendances (mais vous pouvez facilement la transformer en projet web par configuration si vous vous familiarisez avec Spring). REMARQUE: Pour une approche de résolution de méthode basée sur Spring AOP Transactional (sans annotation @Transactional), veuillez consulter ce tutoriel: Gestion des transactions Spring ORM AOP. L’image ci-dessous montre notre projet final d’exemple Spring ORM.
Passons en revue chacun des composants du projet d’exemple Spring ORM un par un.
Dépendances Maven Spring ORM
Voici notre fichier pom.xml final ayant les dépendances Spring ORM. Nous avons utilisé Spring 4 et Hibernate 4 dans notre exemple Spring ORM.
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>hu.daniel.hari.learn.spring</groupId>
<artifactId>Tutorial-SpringORMwithTX</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<!-- Generic properties -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
<!-- SPRING & HIBERNATE / JPA -->
<spring.version>4.0.0.RELEASE</spring.version>
<hibernate.version>4.1.9.Final</hibernate.version>
</properties>
<dependencies>
<!-- LOG -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- JPA Vendor -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- IN MEMORY Database and JDBC Driver -->
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
- Nous avons besoin de
spring-context
et despring-orm
en tant que dépendances Spring. - Nous utilisons
hibernate-entitymanager
pour Hibernate en tant qu’implémentation JPA.hibernate-entitymanager
dépend dehibernate-core
, c’est pourquoi nous n’avons pas besoin d’inclure hibernate-core explicitement dans le fichier pom.xml. Il est intégré à notre projet grâce aux dépendances transitives de Maven. - Nous avons également besoin du pilote JDBC en tant que dépendance pour l’accès à la base de données. Nous utilisons HSQLDB qui contient le pilote JDBC et une base de données en mémoire fonctionnelle.
Classe de modèle Spring ORM
Nous pouvons utiliser les annotations JPA standard pour le mapping dans nos beans de modèle car Hibernate fournit une implémentation JPA.
package hu.daniel.hari.learn.spring.orm.model;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Product {
@Id
private Integer id;
private String name;
public Product() {
}
public Product(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Product [id=" + id + ", name=" + name + "]";
}
}
Nous utilisons les annotations @Entity
et @Id
JPA pour qualifier notre POJO en tant qu’entité et pour définir sa clé primaire.
Classe DAO Spring ORM
Nous créons une classe DAO très simple qui fournit des méthodes persist et findAll.
package hu.daniel.hari.learn.spring.orm.dao;
import hu.daniel.hari.learn.spring.orm.model.Product;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Component;
@Component
public class ProductDao {
@PersistenceContext
private EntityManager em;
public void persist(Product product) {
em.persist(product);
}
public List<Product> findAll() {
return em.createQuery("SELECT p FROM Product p").getResultList();
}
}
- @Component est une annotation Spring qui indique au conteneur Spring que nous pouvons utiliser cette classe via l’inversion de contrôle (Injection de dépendances) de Spring.
- Nous utilisons l’annotation JPA
@PersistenceContext
qui indique l’injection de dépendance vers un EntityManager. Spring injecte une instance appropriée de l’EntityManager selon la configuration spring.xml.
Classe de service Spring ORM
Notre classe de service simple a 2 méthodes d’écriture et 1 méthode de lecture – add, addAll et listAll.
package hu.daniel.hari.learn.spring.orm.service;
import hu.daniel.hari.learn.spring.orm.dao.ProductDao;
import hu.daniel.hari.learn.spring.orm.model.Product;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class ProductService {
@Autowired
private ProductDao productDao;
@Transactional
public void add(Product product) {
productDao.persist(product);
}
@Transactional
public void addAll(Collection<Product> products) {
for (Product product : products) {
productDao.persist(product);
}
}
@Transactional(readOnly = true)
public List<Product> listAll() {
return productDao.findAll();
}
}
- Nous utilisons l’annotation Spring @Autowired pour injecter ProductDao dans notre classe de service.
- Nous voulons utiliser la gestion des transactions, donc les méthodes sont annotées avec
@Transactional
de Spring. La méthode listAll ne fait que lire la base de données, nous définissons donc l’annotation @Transactional en lecture seule pour l’optimisation.
Exemple de configuration XML du bean Spring ORM
Nos classes Java du projet exemple Spring ORM sont prêtes, regardons maintenant notre fichier de configuration de bean Spring. spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:p="https://www.springframework.org/schema/p"
xmlns:context="https://www.springframework.org/schema/context"
xmlns:tx="https://www.springframework.org/schema/tx"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
https://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-3.0.xsd
https://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans -->
<context:component-scan base-package="hu.daniel.hari.learn.spring" />
<!-- Activates various annotations to be detected in bean classes e.g: @Autowired -->
<context:annotation-config />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem://productDb" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:packagesToScan="hu.daniel.hari.learn.spring.orm.model"
p:dataSource-ref="dataSource"
>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<!-- Transactions -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
- Tout d’abord, nous indiquons à Spring que nous voulons utiliser l’analyse du chemin de classe pour les composants Spring (Services, DAOs) plutôt que de les définir un par un dans le fichier spring xml. Nous avons également activé la détection des annotations Spring.
- Ajout de la source de données, qui est actuellement la base de données en mémoire HSQLDB.
- Nous configurons une
EntityManagerFactory
JPA qui sera utilisée par l’application pour obtenir un EntityManager. Spring prend en charge 3 façons différentes de le faire, nous avons utiliséLocalContainerEntityManagerFactoryBean
pour bénéficier de toutes les fonctionnalités JPA. Nous définissons les attributs deLocalContainerEntityManagerFactoryBean
comme suit:- l’attribut packagesToScan qui pointe vers le package de nos classes modèle.
- la source de données définie précédemment dans le fichier de configuration Spring.
- jpaVendorAdapter comme Hibernate et en définissant certaines propriétés Hibernate.
- Nous créons une instance de gestionnaire de transactions Spring PlatformTransactionManager en tant que JpaTransactionManager. Ce gestionnaire de transactions est approprié pour les applications qui utilisent une seule EntityManagerFactory JPA pour l’accès aux données transactionnelles.
- Nous activons la configuration du comportement transactionnel basé sur les annotations, et nous définissons le transactionManager que nous avons créé.
Programme d’exemple de test Spring ORM Hibernate JPA
Notre projet d’exemple Spring ORM JPA Hibernate est prêt, alors écrivons un programme de test pour notre application.
public class SpringOrmMain {
public static void main(String[] args) {
//Créer le contexte de l'application Spring
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");
//Obtenir le service du contexte. (la dépendance du service (ProductDAO) est câblée automatiquement dans ProductService)
ProductService productService = ctx.getBean(ProductService.class);
//Effectuer des opérations sur les données
productService.add(new Product(1, "Bulb"));
productService.add(new Product(2, "Dijone mustard"));
System.out.println("listAll: " + productService.listAll());
//Tester l'annulation de la transaction (clé en double)
try {
productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
} catch (DataAccessException dataAccessException) {
}
//Tester la liste des éléments après l'annulation
System.out.println("listAll: " + productService.listAll());
ctx.close();
}
}
Vous pouvez voir à quel point il est facile de démarrer le conteneur Spring à partir d’une méthode principale. Nous obtenons notre premier point d’entrée injecté par dépendance, l’instance de la classe de service. La référence de la classe ProductDao
est injectée dans la classe ProductService
après l’initialisation du contexte Spring. Après avoir obtenu l’instance de ProductService
, nous pouvons tester ses méthodes, tous les appels de méthode seront transactionnels grâce au mécanisme de proxy de Spring. Nous testons également l’annulation dans cet exemple. Si vous exécutez le programme de test d’exemple ORM Spring ci-dessus, vous obtiendrez les journaux ci-dessous.
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: select product0_.id as id0_, product0_.name as name0_ from Product product0_
listAll: [Product [id=1, name=Bulb], Product [id=2, name=Dijone mustard]]
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: select product0_.id as id0_, product0_.name as name0_ from Product product0_
listAll: [Product [id=1, name=Bulb], Product [id=2, name=Dijone mustard]]
Notez que la deuxième transaction est annulée, c’est pourquoi la liste des produits n’a pas changé. Si vous utilisez le fichier log4j.properties
fourni, vous pouvez voir ce qui se passe sous le capot. Références : https://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html Vous pouvez télécharger le projet d’exemple final Spring ORM JPA Hibernate à partir du lien ci-dessous et jouer avec pour en apprendre davantage.
Source:
https://www.digitalocean.com/community/tutorials/spring-orm-example-jpa-hibernate-transaction