Introduction
This post explains how the Spring Transaction Propagation Attributes -- Propagation.Requires_New and Propagation. Required -- are different. You need to read this post thoroughly to understand. I have also explained JPA technology and Spring technology.
Propagation.REQUIRES_NEW v/s Propagation.REQUIRED
Technology
JPA stands for the Java Persistence API which is the standard from Sun Microsystems for persistence. ORM stands for Object Relational Mapping. JPA is the standard for ORM. If a framework has to say ORM, then it should implement all the specifications given by JPA. JPA is just specification. It needs a persistence provider for CRUD operations. Hibernate, Eclipse link, etc are examples of persistence providers. "Spring" is one of the widely used enterprise application frameworks that give immense integration support to the existing tools for
java web development, GUI, and mobile as well.
Technologies
Spring, JPA and Hibernate, Maven, MySQL database
Usecase
If you are working in spring transaction management, then you will be seeing @Transactional annotation on top of either method or class, which indicates that spring will take care of entity persistence. But, there are several attributes which will affect the transaction execution.
For example
PROPAGATION_SUPPORTS, PROPAGATION_REQUIRES_NEW, PROPAGATION.REQUIRED etc. and many others are there.
In this document, you will find the two attributes' usage: Propagation.REQUIRED and Propagation.REQUIRES_NEW. The main difference between them is if a method in Spring Business Activity/DAO class is annotated with Propagation.REQUIRES_NEW, then when the execution comes to this method, it will create a new transaction irrespective of the existing transaction whereas if the method is annotated with Propagation.REQUIRED, spring checks for any existing transaction. If yes, it uses that old transaction otherwise it creates a new one.
But, the disadvantage of Propagation.REQUIRES_NEW is that even if the inner method fails to execute (because of some exception), the outer method commits the transaction. That causes inconsistency in data. If you use Propagation.REQUIRED, then if both inner/outer methods execute without fail, then only the data will be persisted to the database.
Let’s prove this. For that, we need 5 steps.
There are 4 steps to create the page.
- Create the project with JPA/ Hibernate, Spring.
- Create persistence.xml file, Spring.xml.
- Create Entity, Business classes
- Run the application.
Step 1 Create a project structure
The project structure will be like the above.
If you look at the project structure, it’s a maven project and POM.XML is the mandatory file in it. In this maven file, we can configure dependencies for JPA, hibernate, Java, database, spring etc. I am using a MySQL database for data storage.
Three packages are needed - one for an entity, one for business logic, one for application testing. There’s a spring.xml file which is used to define Business Activity/DAO classes, data sources, entity manager factory, etc. And, the persistence.xml file is used to define persistence unit and its credentials.
Step 2 - spring.xml
- <?xmlversion="1.0"encoding="UTF-8"?>
- <beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans
- http:
- http:
- http:
- http:
- http:
-
- <tx:annotation-driven/>
- <context:component-scanbase-package="jpa.transaction.scopes.business" />
- <beanid="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <propertyname="driverClassName" value="com.mysql.jdbc.Driver" />
- <propertyname="url" value="jdbc:mysql://localhost:3306/employee" />
- <propertyname="username" value="root" />
- <propertyname="password" value="root" />
- </bean>
-
- <beanid="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
- <propertyname="persistenceUnitName" value="jpa-example" />
- <propertyname="dataSource" ref="dataSource" />
- </bean>
- <beanid="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
- <propertyname="entityManagerFactory" ref="entityManagerFactory" />
- </bean>
- <beanid="empBA" class="jpa.transaction.scopes.business.EmpBABean">
- </bean>
- <beanid="employeeBA" class="jpa.transaction.scopes.business.EmployeeBABean">
- <propertyname="empBA" ref="empBA" />
- </bean>
- </beans>
Here, we are saying that transacting the data source will be defined and the data source will be injected by spring framework to the entity manager factory. Transaction manager will be from spring.
Business Activity/DAOs and their dependencies are defined. Spring will inject the dependencies.
Persistence.xml
- <persistencexmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
- http:
- <persistence-unitname="jpa-example" transaction-type="RESOURCE_LOCAL">
- <provider>org.hibernate.ejb.HibernatePersistence</provider>
-
- </persistence-unit>
- </persistence>
Here, the persistence provider is defined. As we have defined the data source in spring.xml file already, there is no need to define JDBC attributes here.
Step 3 - EmployeeBE.java
This is the entity class which is going to be persisted in the database.
-
-
- @NamedQueries({
- @NamedQuery(name = EmployeeBE.FIND_ALL, query = "SELECT e FROM EmployeeBE e order by e.name "),
- })
- @Entity
- @Table(name = "T_EMP")
- publicclassEmployeeBEimplements Serializable{
- privatestaticfinallongserialVersionUID = 1607726899931733607L;
-
- publicstaticfinal String FIND_ALL = "naveen.examples.jpa.entity.EmployeeBE.find_all";
-
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- privateintid;
-
- @Column(name = "NAME")
- private String name;
-
- @Column(name = "version_num")
- @Version
- privateintversion;
-
-
-
-
- packagejpa.transaction.scopes.business;
- importjpa.transaction.scopes.entity.EmployeeBE;
- publicinterfaceEmpBA {
- publicvoidsaveEmployee(EmployeeBEnewEmp);
- }
- packagejpa.transaction.scopes.business;
- importjavax.persistence.EntityManager;
- importjavax.persistence.PersistenceContext;
-
- importorg.springframework.transaction.annotation.Propagation;
- importorg.springframework.transaction.annotation.Transactional;
-
- importjpa.transaction.scopes.entity.EmployeeBE;
-
- publicclassEmpBABeanimplementsEmpBA {
-
- @PersistenceContext
- privateEntityManagerentityManager;
-
- @Transactional(propagation = Propagation.REQUIRES_NEW)
- publicvoidsaveEmployee(EmployeeBEnewEmp) {
- entityManager.persist(newEmp);
- thrownewIllegalArgumentException("Exception Occured");
- }
-
- }
-
- packagejpa.transaction.scopes.business;
- importjpa.transaction.scopes.entity.EmployeeBE;
- publicinterfaceEmployeeBA {
- publicvoidsaveUser(EmployeeBEemployeeBE);
-
- }
-
-
- packagejpa.transaction.scopes.business;
-
- importjavax.persistence.EntityManager;
- importjavax.persistence.PersistenceContext;
-
- importorg.springframework.transaction.annotation.Propagation;
- importorg.springframework.transaction.annotation.Transactional;
-
- importjpa.transaction.scopes.entity.EmployeeBE;
-
- @Transactional
- publicclassEmployeeBABeanimplementsEmployeeBA {
-
- @PersistenceContext
- privateEntityManagerentityManager;
-
- privateEmpBAempBA;
-
- @Transactional(propagation = Propagation.REQUIRES_NEW)
- publicvoidsaveUser(EmployeeBEemployeeBE) {
- entityManager.persist(employeeBE);
- try{
- EmployeeBEnewEmp = newEmployeeBE();
- newEmp.setName("Sachin");
- empBA.saveEmployee(newEmp);
- }catch(Exception e){
- e.printStackTrace();
- }
- }
-
- publicEmpBAgetEmpBA() {
- returnempBA;
- }
-
- publicvoidsetEmpBA(EmpBAempBA) {
- this.empBA = empBA;
- }
- }
If you look at the above code snippets, we have,
- Two Interface, EmpBA, EmployeeBAdefines business methods
- EmpBABean, EmployeeBABean, implementation of business methods
In the Business Activity/DAO classes, if you observe, these classes are annotated with @Transactional; that means the transaction will be handled by spring, like beginning the transaction, committing and closing the transaction.
Methods are annotated with propagation attribute REQUIRED_NEW in both classes.
POM.xml
- <projectxmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>jpa</groupId>
- <artifactId>transaction.scopes</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <packaging>jar</packaging>
-
- <name>transaction.scopes</name>
- <url>http:
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <spring.version>3.2.5.RELEASE</spring.version>
- <hibernate.version>4.1.9.Final</hibernate.version>
- </properties>
-
- <dependencies>
-
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-core</artifactId>
- <version>${spring.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>${spring.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-tx</artifactId>
- <version>${spring.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-orm</artifactId>
- <version>${spring.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.hibernate</groupId>
- <artifactId>hibernate-core</artifactId>
- <version>${hibernate.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.hibernate</groupId>
- <artifactId>hibernate-entitymanager</artifactId>
- <version>${hibernate.version}</version>
- </dependency>
-
- <dependency>
- <groupId>commons-dbcp</groupId>
- <artifactId>commons-dbcp</artifactId>
- <version>1.2.2</version>
- </dependency>
-
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>5.1.31</version>
- </dependency>
-
- </dependencies>
- </project>
Dependencies for spring, hibernate, MySQL will be defined here. Spring ORM, spring transaction management dependencies, and core modules will be defined here.
Step 4 - Run the application
- packagejpa.transaction.scopes;
-
- importorg.springframework.context.ApplicationContext;
- importorg.springframework.context.support.ClassPathXmlApplicationContext;
-
- importjpa.transaction.scopes.business.EmployeeBA;
- importjpa.transaction.scopes.entity.EmployeeBE;
-
- publicclassApplcation {
-
- @SuppressWarnings("resource")
- publicstaticvoid main(String[] args) {
-
- ApplicationContextctx = newClassPathXmlApplicationContext(
- "spring.xml");
-
- EmployeeBAuserActivityBA = (EmployeeBA) ctx.getBean("employeeBA");
-
- EmployeeBEemployeeBE = newEmployeeBE();
- employeeBE.setName("Naveen");
-
- userActivityBA.saveUser(employeeBE);
- }
- }
Let’s say, you are not throwing any exception in EmpBABean, as it is being called from EmployeeBABean.java.
If you see, two records have been inserted. Now, you change both the method propagations to
@Transactional (propagation = Propagation.REQUIRED) and execute the application.
Two more new records have been inserted.
Now, throw the error in EmpBABean.java and run the application.
No new record has been created because there is an error in inner BA class which causes the whole transaction to be rolled back there. And, no record has been persisted to the database.
Now, change the propagation to REQUIRED_NEW and throw the exception and run the application.
If you observe, a new record with id 63 has been created. This is from Outer Business Activity/DAO class EmployeeBABean.java. As the inner BA method has thrown some exception, its data has not been committed to the database. But, the outer BA method is propagated with REQUIRED_NEW, so its data is persisted to a database.
Conclusion
These spring transaction attributes play a major role depending on the requirement. Sometimes, there may be cases where irrespective of other entity persistence, the current entity needs to persist. In that case, REQUIRED_NEW is very useful. But, this is a very rare case as it causes data inconsistency. In general, REQUIRED is used for the transaction.
Everything shared by java web development experts in this post is according to their practice and experience.
You can share your thoughts and experience with other readers through the comments. Ask questions directly from experts and get a response.