Benvenuti al Tutorial di Esempio di Spring ORM. Oggi esamineremo un esempio di Spring ORM utilizzando la gestione delle transazioni Hibernate JPA. Ti mostrerò un esempio molto semplice di un’applicazione stand-alone Spring con le seguenti caratteristiche.
- Iniezione di dipendenze (annotazione @Autowired)
- JPA EntityManager (fornito da Hibernate)
- Metodi transazionali annotati (annotazione @Transactional)
Esempio di Spring ORM
Ho utilizzato un database in memoria per l’esempio di Spring ORM, quindi non c’è bisogno di alcuna configurazione del database (ma è possibile cambiarlo in qualsiasi altro database nella sezione datasource di spring.xml). Questa è un’applicazione standalone di Spring ORM per minimizzare tutte le dipendenze (ma è possibile cambiarla facilmente in un progetto web tramite configurazione se ti familiarizzi con Spring). NOTA: Per un approccio di risoluzione del metodo basato su Spring AOP Transactional (senza annotazione @Transactional), consulta questo tutorial: Gestione transazionale di Spring ORM AOP. L’immagine sottostante mostra il nostro progetto finale di esempio Spring ORM.
Esaminiamo uno per uno ciascuno dei componenti del progetto di esempio Spring ORM.
Dependencies Maven di Spring ORM
Di seguito è riportato il nostro file pom.xml finale con le dipendenze di Spring ORM. Abbiamo utilizzato Spring 4 e Hibernate 4 nel nostro esempio di 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>
- Abbiamo bisogno di
spring-context
espring-orm
come dipendenze di Spring. - Utilizziamo
hibernate-entitymanager
come implementazione JPA di Hibernate.hibernate-entitymanager
dipende dahibernate-core
per questo motivo non dobbiamo inserire esplicitamente hibernate-core nel pom.xml. Viene incluso nel nostro progetto tramite le dipendenze transitive di Maven. - Abbiamo anche bisogno di un driver JDBC come dipendenza per l’accesso al database. Stiamo utilizzando HSQLDB che contiene il driver JDBC e un database in memoria funzionante.
Classe Modello Spring ORM
Possiamo utilizzare le annotazioni standard JPA per il mapping nei nostri bean del modello perché Hibernate fornisce l’implementazione 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 + "]";
}
}
Utilizziamo le annotazioni @Entity
e @Id
per definire la nostra classe POJO come entità e per definire la sua chiave primaria.
Classe DAO Spring ORM
Creiamo una classe DAO molto semplice che fornisce i metodi persist e 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 è un’annotazione di Spring che indica al container di Spring che possiamo utilizzare questa classe tramite l’Inversione di Controllo (Dependency Injection) di Spring.
- Utilizziamo l’annotazione JPA
@PersistenceContext
che indica l’iniezione di dipendenza verso un EntityManager. Spring inietta un’istanza appropriata di EntityManager in base alla configurazione di spring.xml.
Classe di servizio Spring ORM
La nostra semplice classe di servizio ha 2 metodi di scrittura e 1 metodo di lettura – add, addAll e 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();
}
}
- Utilizziamo l’annotazione Spring @Autowired per iniettare ProductDao nella nostra classe di servizio.
- Vogliamo utilizzare la gestione delle transazioni, quindi i metodi sono annotati con l’annotazione Spring
@Transactional
. Il metodo listAll legge solo dal database, quindi impostiamo l’annotazione @Transactional come readonly per ottimizzazione.
Esempio di configurazione del bean Spring ORM XML
I nostri file di classi Java di esempio di Spring ORM sono pronti, ora diamo un’occhiata al nostro file di configurazione del 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>
- Prima diciamo a Spring che vogliamo utilizzare la scansione del classpath per i componenti Spring (Servizi, DAO) anziché definirli uno per uno in spring xml. Abbiamo anche abilitato il rilevamento delle annotazioni Spring.
- Aggiungendo la fonte dati, che attualmente è il database in memoria HSQLDB.
- Abbiamo configurato un
EntityManagerFactory
JPA che verrà utilizzato dall’applicazione per ottenere un EntityManager. Spring supporta 3 diverse modalità per farlo, abbiamo utilizzatoLocalContainerEntityManagerFactoryBean
per le funzionalità complete di JPA. Abbiamo impostato gli attributi diLocalContainerEntityManagerFactoryBean
come segue:- attributo packagesToScan che punta al pacchetto delle nostre classi modello.
- datasource definita in precedenza nel file di configurazione di Spring.
- jpaVendorAdapter come Hibernate e impostando alcune proprietà di Hibernate.
- Creiamo un’istanza di Spring PlatformTransactionManager come JpaTransactionManager. Questo gestore delle transazioni è adatto per le applicazioni che utilizzano un singolo EntityManagerFactory JPA per l’accesso ai dati transazionali.
- Abilitiamo la configurazione del comportamento transazionale basato su annotazioni e impostiamo il transactionManager che abbiamo creato.
Esempio di programma di test Spring ORM Hibernate JPA
Il nostro progetto di esempio di Spring ORM JPA Hibernate è pronto, quindi scriviamo un programma di test per la nostra applicazione.
public class SpringOrmMain {
public static void main(String[] args) {
//Creare contesto dell'applicazione Spring
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");
//Ottenere servizio dal contesto. (la dipendenza del servizio (ProductDAO) è autowired in ProductService)
ProductService productService = ctx.getBean(ProductService.class);
//Effettuare alcune operazioni sui dati
productService.add(new Product(1, "Bulb"));
productService.add(new Product(2, "Dijone mustard"));
System.out.println("listAll: " + productService.listAll());
//Test rollback della transazione (chiave duplicata)
try {
productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
} catch (DataAccessException dataAccessException) {
}
//Test lista degli elementi dopo il rollback
System.out.println("listAll: " + productService.listAll());
ctx.close();
}
}
Puoi vedere quanto sia facile avviare il contenitore Spring da un metodo principale. Stiamo ottenendo il nostro primo punto di ingresso per l’iniezione delle dipendenze, l’istanza della classe di servizio. Il riferimento della classe ProductDao
è iniettato nella classe ProductService
dopo che il contesto di Spring è stato inizializzato. Dopo aver ottenuto l’istanza di ProductService
, possiamo testare i suoi metodi, tutte le chiamate ai metodi saranno transazionali grazie al meccanismo di proxy di Spring. Testiamo anche il rollback in questo esempio. Se esegui il programma di test dell’esempio di ORM di primavera sopra, otterrai i log seguenti.
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]]
Nota che la seconda transazione viene annullata, ecco perché la lista dei prodotti non è cambiata. Se utilizzi il file log4j.properties
dall’origine allegata, puoi vedere cosa succede sotto il cofano. Riferimenti: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html Puoi scaricare il progetto di esempio finale di Spring ORM JPA Hibernate dal seguente link e giocarci per imparare di più.
Source:
https://www.digitalocean.com/community/tutorials/spring-orm-example-jpa-hibernate-transaction