Spring ORM Example – Hibernate JPA Transaction
Welcome to the Spring ORM Example Tutorial. Today we will look into Spring ORM example using Hibernate JPA transaction management. I will show you a very simple example of Spring standalone application with following features.
- Dependency Injection (@Autowired annotation)
- JPA EntityManager (provided by Hibernate)
- Annotated transactional methods (@Transactional annotation)
Spring ORM Example
I have used in-memory database for Spring ORM example, so no need for any database setup (but you can change it to any other database in the spring.xml datasource section). This is a Spring ORM standalone application to minimize all dependencies (but you can easily change it to a web project by configuration if you get familiar with spring).
Let’s go through each of the Spring ORM example project components one by one.
Spring ORM Maven dependencies
Below is our final pom.xml file having Spring ORM dependencies. We have used Spring 4 and Hibernate 4 in our Spring ORM example.
<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 need spring-context and spring-orm as Spring dependencies.
- We use hibernate-entitymanager for Hibernate as JPA implementation. hibernate-entitymanager is dependent on hibernate-core this why we don’t have to put hibernate-core in pom.xml explicitly. It’s being pulled into our project through maven transitive dependencies.
- We also need JDBC driver as dependency for database access. We are using HSQLDB that contains the JDBC driver and a working in memory database.
Spring ORM Model Class
We can use standard JPA annotations for mapping in our model beans because Hibernate provides JPA implementation.
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 use @Entity and @Id JPA annotations to qualify our POJO as an Entity and to define its primary key.
Spring ORM DAO Class
We create a very simple DAO class that provides persist and findAll methods.
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 findAll() {
return em.createQuery("SELECT p FROM Product p").getResultList();
}
}
@Component is a Spring annotation that tells the Spring container that we can use this class through Spring IoC (Dependency Injection).
We use the JPA @PersistenceContext annotation to indicate dependency injection to an EntityManager. Spring injects a proper instance of EntityManager according to the spring.xml configuration.
Spring ORM Service Class
Our simple service class has 2 write and 1 read methods – add, addAll, and 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 products) {
for (Product product : products) {
productDao.persist(product);
}
}
@Transactional(readOnly = true)
public List listAll() {
return productDao.findAll();
}
}
We use Spring @Autowired annotation to inject ProductDao in our service class.
We want to use transaction management, so methods are annotated with @Transactional Spring annotation. The listAll method only reads the database so we set the @Transactional annotation to read-only for optimization.
Spring ORM Example Bean Configuration XML
Our spring ORM example project java classes are ready, let’s look at our spring bean configuration file now. 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>
First, we tell spring that we want to use classpath scanning for Spring components (Services, DAOs) rather than defining them one by one in spring xml. We have also enabled Spring annotation detection.
Adding the datasource, that is currently HSQLDB in-memory database.
We set up a JPA EntityManagerFactory that will be used by the application to obtain an EntityManager. Spring supports 3 different ways to do this, we have used LocalContainerEntityManagerFactoryBean for full JPA capabilities. We set LocalContainerEntityManagerFactoryBean attributes as:
- packagesToScan attribute that points to our model classes package.
- datasource defined earlier in the spring configuration file.
- jpaVendorAdapter as Hibernate and setting some hibernate properties.
We create a Spring PlatformTransactionManager instance as a JpaTransactionManager. This transaction manager is appropriate for applications that use a single JPA EntityManagerFactory for transactional data access.
We enable the configuration of transactional behavior based on annotations, and we set the transactionManager we created.
Spring ORM Hibernate JPA Example Test Program
Our spring ORM JPA Hibernate example project is ready, so let’s write a test program for our application.
public class SpringOrmMain {
public static void main(String[] args) {
//Create Spring application context
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");
//Get service from context. (service's dependency (ProductDAO) is autowired in ProductService)
ProductService productService = ctx.getBean(ProductService.class);
//Do some data operation
productService.add(new Product(1, "Bulb"));
productService.add(new Product(2, "Dijone mustard"));
System.out.println("listAll: " + productService.listAll());
//Test transaction rollback (duplicated key)
try {
productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
} catch (DataAccessException dataAccessException) {
}
//Test element list after rollback
System.out.println("listAll: " + productService.listAll());
ctx.close();
}
}
You can see how easily we can start the Spring container from a main method. We are getting our first dependency injected entry point, the service class instance. ProductDao class reference injected to the ProductService class after the spring context is initialized. After we got ProductService instance, we can test its methods, all method calls will be transactional due to Spring’s proxy mechanism. We also test rollback in this example.
If you run the above spring ORM example test program, you will get the following 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]]
Note that the second transaction is rolled back, this is why the product list didn’t change.