Back to: Spring Framework Tutorials
Spring Framework Declarative Transaction Management
In this article, I am going to discuss Spring Framework Declarative Transaction Management with Examples. Please read our previous article, where we discussed Spring Framework Programmatic Transaction Management.
What is Declarative Transaction Management in Spring Framework?
Declarative Transaction Management in Spring Framework is an approach in which transactions are managed through configuration rather than explicit coding within the application logic. In this approach, developers specify transaction-related information using annotations or XML-based configuration files, and the underlying framework automatically handles the transaction management.
In the context of the Spring Framework, declarative transaction management is achieved through the use of annotations such as @Transactional. By applying the @Transactional annotation to methods or classes, developers can indicate that the annotated code should be executed within a transactional context.
How Declarative Transaction Management Works in Spring Framework?
- Enable Transaction Management: To enable declarative transaction management in Spring, you need to configure the appropriate transaction manager and enable transaction annotations. This is typically done through XML-based configuration or by using Java-based configuration classes.
- Apply Transaction Annotations: Once transaction management is enabled, you can apply the @Transactional annotation to the methods or classes that require transactional behavior. By doing so, you delegate the responsibility of managing transactions to the underlying Spring transaction infrastructure.
- Transactional Behavior: When a method annotated with @Transactional is invoked, the Spring framework intercepts the method call and automatically starts a transaction before the method execution. If the method completes without throwing an exception, the framework commits the transaction. If an exception is thrown, the framework rolls back the transaction.
- Configuration Options: The @Transactional annotation provides various configuration options to control transaction behavior. For example, you can specify the propagation behavior, isolation level, timeout, read-only status, and rollback rules for specific exceptions.
Declarative transaction management simplifies the code by separating the transaction management concerns from the application logic. It allows developers to focus on business logic without the need to write explicit transaction handling code. This approach promotes a more modular and reusable code structure, as transaction management can be applied consistently across multiple methods or classes.
Declarative transaction management also offers flexibility as transactional behavior can be easily configured and modified without modifying the application code. Developers can change the transaction configuration by modifying the annotations or configuration files, rather than modifying the actual business logic.
Additionally, declarative transaction management enables the use of AOP (Aspect-Oriented Programming) techniques to provide cross-cutting concerns such as logging, security, or caching around the transactional behavior.
Overall, declarative transaction management in Spring simplifies transaction handling, promotes modularity, and allows for easy configuration changes. It is a powerful approach that enhances the maintainability and readability of the codebase.
Setting up MySQL
Before we use transaction management, we will need to have a database server. We will use MySQL for this example.
Step 1: Download, install, and set up MySQL. You may use MySQL Workbench or MySQL shell. Here we will use MySQL shell:
Step 2: Create a database using the CREATE DATABASE command:
Check that the database has been created:
Step 3: Set the newly created springbootdb database to the current database:
Step 4: Create tables called Employee and Salary:
Implementing Programmatic Transaction Management in Spring Framework
Before starting the project, ensure that the required JAR files are in the projectās āReferenced Librariesā folder.
Apart from these JAR files, another JAR file is required for JDBC. This JAR file is the connector to the MySQL server. It can be found at https://dev.mysql.com/downloads/connector/j/. Select āPlatform Independentā as the operating system. Download the archive file (as ZIP or TAR). Extract it (to obtain a JAR file) and paste the JAR into the āReferenced Librariesā directory.
Step 1: Create a new file called Employee.java in the src/ directory. This file has the exact same contents as the file from the previous article (Programmatic Transaction Management).
public class Employee { private int id, age, salary, year; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public Employee() { } public Employee(int id, int age, int salary, int year, String name) { this.id = id; this.age = age; this.salary = salary; this.year = year; this.name = name; } @Override public String toString() { return "Employee [id=" + id + ", age=" + age + ", salary=" + salary + ", year=" + year + ", name=" + name + "]"; } }
Step 2: Create a new file called EmployeeDAO.java in the src/ directory. This file has the exact same contents as the file from the previous article (Programmatic Transaction Management)
import java.util.List; import javax.sql.DataSource; public interface EmployeeDAO { public void setDataSource(DataSource ds); public void create (String name, int age, int salary, int year); public List<Employee> getAllEmployees (); }
Step 3: Create a new file called EmployeeMapper.java in the src/ directory. This file has the exact same contents as the file from the previous article (Programmatic Transaction Management).
import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.jdbc.core.RowMapper; public class EmployeeMapper implements RowMapper<Employee> { @Override public Employee mapRow(ResultSet arg0, int arg1) throws SQLException { return new Employee(arg0.getInt("id"), arg0.getInt("age"), arg0.getInt("sal"), arg0.getInt("year"), arg0.getString("name")); } }
This class implements the RowMapper interface. This is responsible for creating a new object of type Employee from the SQL result.
Step 4: Create a new file called EmployeeJDBC.java in the src/ directory. Add the following contents to the file:
import java.util.List; import javax.sql.DataSource; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; public class EmployeeJDBC implements EmployeeDAO { private JdbcTemplate jdbcTemplate; @Override public void setDataSource(DataSource ds) {this.jdbcTemplate = new JdbcTemplate(ds);} @Override public void create(String name, int age, int salary, int year) { try { String SQL = "INSERT INTO Employee(name,age) VALUES (?,?)"; jdbcTemplate.update(SQL, name, age); SQL = "SELECT MAX(id) FROM Employee"; int id = jdbcTemplate.queryForObject(SQL, new Object[] {}, Integer.class); SQL = "INSERT INTO Salary(employee_id,sal,year) VALUES (?,?,?)"; jdbcTemplate.update(SQL, id, salary, year); System.out.println("Created Name: " + name + ", Age: " + age); } catch (DataAccessException e) { System.out.println("Error in adding data, rolling back."); throw e; } } @Override public List<Employee> getAllEmployees() { String SQL = "SELECT * FROM Employee, Salary WHERE Employee.id = Salary.employee_id"; return jdbcTemplate.query(SQL, new EmployeeMapper()); } }
In this file, we have implemented the functions defined in the EmployeeDAO interface. This file is also similar to the file from the previous project. However, the only difference is that there is no TransactionManager object which handles the transaction management. This transaction manager will be implemented in another file (Beans.xml).
Step 5: In the src/ there must be a file called App.java. This file contains the main function and was created when the project was created (by VS Code). This file has the exact same contents as the file from the previous article (Programmatic Transaction Management).
import java.util.List; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); EmployeeJDBC jdbc = (EmployeeJDBC) context.getBean("jdbc"); System.out.println("Adding data ..."); jdbc.create("Employee A", 25, 500000, 2020); jdbc.create("Employee B", 35, 1000000, 2021); jdbc.create("Employee C", 45, 2000000, 2022); System.out.println(); System.out.println("Printing data ..."); List<Employee> employees = jdbc.getAllEmployees(); for (Employee e : employees) System.out.println(e.toString()); System.out.println(); } }
The main function creates an EmployeeJDBC bean from the Beans.xml file. We first add some data into the database before printing it.
Step 6: Create a new file called Beans.xml in the src/ directory. Add the following contents to the file:
<?xml version = "1.0" encoding = "UTF-8"?> <beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd "> <!-- Initialization for data source --> <bean id="dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name = "driverClassName" value = "com.mysql.cj.jdbc.Driver" /> <property name = "url" value = "jdbc:mysql://localhost:3306/springframeworkdb"/> <property name = "username" value = "root"/> <property name = "password" value = "DotNet23"/> </bean> <tx:advice id = "txAdvice" transaction-manager = "transactionManager"> <tx:attributes> <tx:method name = "create"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id = "createOperation" expression = "execution(* EmployeeJDBC.create(..))"/> <aop:advisor advice-ref = "txAdvice" pointcut-ref = "createOperation"/> </aop:config> <!-- Initialization for TransactionManager --> <bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name = "dataSource" ref = "dataSource" /> </bean> <bean id = "jdbc" class = "EmployeeJDBC"> <property name = "dataSource" ref = "dataSource"/> </bean> </beans>
This file is responsible for defining the data source (which is the MySQL server) and injecting it into the EmployeeJDBC object. This file is also similar to the file from the previous project. However, the only difference is that we have added two more sections to the file (which are in the red box highlighted above). This handles transaction management.
Step 7: Compile and execute the application. Ensure compilation is successful. Ensure that the output is as expected:
As can be seen, all the required SQL operations (CRUD) execute. Congratulations! You have completed the transaction management application in Spring Framework!
In the next article, I am going to discuss Spring Framework Web MVC. Here, in this article, I try to explain Spring Framework Declarative Transaction Management with Examples. I hope you enjoy this Spring Framework Declarative Transaction Management article.
Registration Open For New Online Training
Enhance Your Professional Journey with Our Upcoming Live Session. For complete information on Registration, Course Details, Syllabus, and to get the Zoom Credentials to attend the free live Demo Sessions, please click on the below links.
Instead of
EmployeeJDBC jdbc = (EmployeeJDBC) context.getBean(ājdbcā);
we need to cast to
EmployeeDAO jdbc= (EmployeeDAO) context.getBean(ājdbcā);
Also in Beans.xml
as Spring AOP creates proxies to manage transactions and these proxies can implement interface. When we retrieve a bean it returns a proxy object rather than an original bean. This proxy implements the interface that target bean implements.