Welkom bij de Spring ORM voorbeeldtutorial. Vandaag zullen we kijken naar een voorbeeld van Spring ORM met behulp van Hibernate JPA-transactiebeheer. Ik zal je een zeer eenvoudig voorbeeld laten zien van een op zichzelf staande Spring-toepassing met de volgende functies.
- Dependency Injection (@Autowired-annotatie)
- JPA EntityManager (geleverd door Hibernate)
- Annotated transactionele methoden (@Transactional-annotatie)
Spring ORM Voorbeeld
Ik heb een in-memory database gebruikt voor het Spring ORM-voorbeeld, dus er is geen behoefte aan enige database-instelling (maar je kunt het wijzigen naar elke andere database in de spring.xml datasource sectie). Dit is een op zichzelf staande Spring ORM-toepassing om alle afhankelijkheden te minimaliseren (maar je kunt het eenvoudig wijzigen naar een webproject door configuratie als je bekend bent met Spring). LET OP: Voor Spring AOP gebaseerde transactie (zonder @Transactional annotatie) methode resolutie benadering, bekijk deze tutorial: Spring ORM AOP Transactiebeheer. Hieronder zie je ons uiteindelijke Spring ORM-voorbeeldproject.
Laten we elk van de componenten van het Spring ORM-voorbeeldproject één voor één doornemen.
Spring ORM Maven-afhankelijkheden
Hieronder staat ons uiteindelijke pom.xml-bestand met Spring ORM-afhankelijkheden. We hebben Spring 4 en Hibernate 4 gebruikt in ons Spring ORM-voorbeeld.
<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>
- We hebben
spring-context
enspring-orm
nodig als Spring-afhankelijkheden. - We gebruiken
hibernate-entitymanager
voor Hibernate als JPA-implementatie.hibernate-entitymanager
is afhankelijk vanhibernate-core
, daarom hoeven we hibernate-core niet expliciet in pom.xml op te nemen. Het wordt in ons project geladen via maven transitive dependencies. - We hebben ook een JDBC-driver nodig als afhankelijkheid voor database-toegang. We gebruiken HSQLDB dat de JDBC-driver bevat en een werkende in-memory database.
Spring ORM Modelklasse
We kunnen standaard JPA-annotaties gebruiken voor het mappen in onze modelbeans omdat Hibernate JPA-implementatie biedt.
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 + "]";
}
}
We gebruiken de JPA-annotaties @Entity
en @Id
om onze POJO te kwalificeren als een entiteit en om de primaire sleutel te definiëren.
Spring ORM DAO-klasse
We maken een zeer eenvoudige DAO-klasse die persist en findALL-methoden biedt.
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 is een Spring-annotatie die de Spring-container vertelt dat we deze klasse kunnen gebruiken via Spring IoC (Dependency Injection).
- We gebruiken de JPA
@PersistenceContext
annotatie die de afhankelijkheidsinjectie naar een EntityManager aangeeft. Spring injecteert een juiste instantie van EntityManager op basis van de spring.xml-configuratie.
Spring ORM-serviceklasse
Onze eenvoudige serviceklasse heeft 2 schrijfmethoden en 1 leesmethode – toevoegen, alles toevoegen en alles vermelden.
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();
}
}
- We gebruiken de Spring @Autowired annotatie om ProductDao in onze serviceklasse te injecteren.
- We willen transactiebeheer gebruiken, dus methoden zijn geannoteerd met
@Transactional
Spring-annotatie. De listAll-methode leest alleen de database, dus we stellen de @Transactional annotatie in op alleen-lezen voor optimalisatie.
Voorbeeld van Spring ORM Bean-configuratie XML
Onze Java-klassen voor het voorbeeldproject van Spring ORM zijn klaar, laten we nu naar ons spring bean-configuratiebestand kijken. 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>
- Eerst vertellen we Spring dat we classpath-scanning willen gebruiken voor Spring-componenten (services, DAO’s) in plaats van ze één voor één te definiëren in de spring-xml. We hebben ook de detectie van Spring-annotaties ingeschakeld.
- Het toevoegen van de gegevensbron, die momenteel de HSQLDB in-memory database is.
- We stellen een JPA
EntityManagerFactory
in die door de applicatie zal worden gebruikt om een EntityManager te verkrijgen. Spring ondersteunt 3 verschillende manieren om dit te doen, we hebbenLocalContainerEntityManagerFactoryBean
gebruikt voor volledige JPA-mogelijkheden. We stellen de attributen vanLocalContainerEntityManagerFactoryBean
als volgt in:- packagesToScan-attribuut dat wijst naar onze modelklassenpakket.
- gegevensbron gedefinieerd eerder in het Spring-configuratiebestand.
- jpaVendorAdapter als Hibernate en instelling van enkele hibernate-eigenschappen.
- We maken een Spring PlatformTransactionManager-instantie aan als een JpaTransactionManager. Deze transactiemanager is geschikt voor toepassingen die een enkele JPA EntityManagerFactory gebruiken voor transactionele gegevenstoegang.
- We maken de configuratie van transactioneel gedrag op basis van annotaties mogelijk, en we stellen de transactionManager in die we hebben gemaakt.
Spring ORM Hibernate JPA Voorbeeld Testprogramma
Ons Spring ORM JPA Hibernate voorbeeldproject is klaar, laten we dus een testprogramma schrijven voor onze applicatie.
public class SpringOrmMain {
public static void main(String[] args) {
//Maak de Spring-toepassingscontext
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");
//Haal service op uit de context. (de afhankelijkheid van de service (ProductDAO) is bedraad in ProductService)
ProductService productService = ctx.getBean(ProductService.class);
//Voer enkele gegevensbewerkingen uit
productService.add(new Product(1, "Bulb"));
productService.add(new Product(2, "Dijone mustard"));
System.out.println("listAll: " + productService.listAll());
//Test transactie-rollback (gedupliceerde sleutel)
try {
productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
} catch (DataAccessException dataAccessException) {
}
//Test elementenlijst na rollback
System.out.println("listAll: " + productService.listAll());
ctx.close();
}
}
Je kunt zien hoe eenvoudig we de Spring-container kunnen starten vanuit een hoofdmethode. We krijgen ons eerste afhankelijkheid-geïnjecteerde instappunt, de serviceklasse-instantie. De verwijzing naar de ProductDao-klasse wordt geïnjecteerd in de ProductService-klasse nadat de Spring-context is geïnitialiseerd. Nadat we de ProducService-instantie hebben gekregen, kunnen we de methoden ervan testen. Alle methodenoproepen zijn transacties vanwege het Spring-proxy-mechanisme. We testen ook rollback in dit voorbeeld. Als je het bovenstaande Spring ORM-voorbeeld-testprogramma uitvoert, krijg je de onderstaande logs.
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]]
Merk op dat de tweede transactie wordt teruggedraaid, daarom is de productlijst niet gewijzigd. Als je het log4j.properties-bestand uit de bijgevoegde bron gebruikt, kun je zien wat er onder de motorkap gebeurt. Referenties: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html Je kunt het definitieve Spring ORM JPA Hibernate voorbeeldproject downloaden via de onderstaande link en ermee spelen om meer te leren.
Source:
https://www.digitalocean.com/community/tutorials/spring-orm-example-jpa-hibernate-transaction