Back to: Spring Framework Tutorials
Spring Framework Programmatic Transaction Management
In this article, I am going to discuss Spring Framework Programmatic Transaction Management with Examples. Please read our previous article, where we discussed Spring Framework Transaction Management.
What is Programmatic Transaction Management?
In Spring, programmatic transaction management refers to the approach of managing transactions explicitly within the application code using the Spring Framework’s transaction management APIs. It gives developers fine-grained control over transaction boundaries, allowing them to start, commit, or rollback transactions based on their specific requirements.
To use programmatic transaction management in Spring, developers typically work with the PlatformTransactionManager interface and its implementations. The PlatformTransactionManager is responsible for managing transactions and providing the necessary methods to control transaction behavior.
How Programmatic Transaction Management Works in Spring Framework?
- Define the TransactionManager: First, we need to configure the PlatformTransactionManager bean in the Spring configuration file. This bean represents the transaction manager that will handle transactional operations. Spring provides various implementations of PlatformTransactionManager depending on the underlying transaction management technology you are using, such as JDBC, JPA, or JTA.
- Begin a Transaction: Within the application code, you can begin a new transaction by obtaining an instance of TransactionStatus from the transaction manager. This instance represents the current transaction status and allows you to control the transaction behavior.
- Perform Database Operations: Once the transaction is started, you can perform database operations using the appropriate data access objects (DAOs) or ORM frameworks like Hibernate. These operations are executed within the transactional context.
- Commit or Rollback the Transaction: After executing the necessary database operations, you can decide whether to commit or rollback the transaction based on the outcome. If all the operations are successful, you can use the commit() method on the TransactionStatus object to commit the transaction. If an error occurs or an exception is thrown, you can use the rollback() method to roll back the transaction and undo any changes made within it.
By using programmatic transaction management, developers have full control over transaction boundaries. They can handle complex transaction scenarios, such as conditional commits or rollbacks based on specific conditions, savepoints within transactions, or performing multiple transactions within a single method.
Programmatic transaction management in Spring provides flexibility, but it requires developers to write transaction handling code explicitly. It is suitable for situations where fine-grained control is needed, or when integrating with existing transactional systems or frameworks that don’t provide declarative transaction management capabilities.
However, it’s important to note that Spring also provides declarative transaction management using annotations or XML-based configuration, which simplifies transaction handling by separating it from the application code. Developers can choose the approach that best suits their application’s needs and development preferences.
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. Add the following contents to the file:
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 + "]"; } }
This class is a POJO class that represents an employee. Note that the setters, getters, constructors, and toString can be added using the “Source Action …” option in the right-click menu of VS Code.
Step 2: Create a new file called EmployeeDAO.java in the src/ directory. Add the following contents to the file:
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. Add the following contents to the file:
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; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; public class EmployeeJDBC implements EmployeeDAO { private DataSource dataSource; private JdbcTemplate jdbcTemplate; private PlatformTransactionManager transactionManager; @Override public void setDataSource(DataSource ds) { this.dataSource = ds; this.jdbcTemplate = new JdbcTemplate(dataSource); } public void setTransactionManager (PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } @Override public void create(String name, int age, int salary, int year) { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); 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); transactionManager.commit(status); } catch (DataAccessException e) { System.out.println("Error in performing transaction: rolling back"); transactionManager.rollback(status); System.out.println(e.getMessage()); } } @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.
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). Modify the file as follows:
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> <!-- 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"/> <property name = "transactionManager" ref = "transactionManager"/> </bean> </beans>
This file is responsible for defining the data source (which is the MySQL server) and injecting it into the EmployeeJDBC object.
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 Declarative Transaction Management. Here, in this article, I try to explain Spring Framework Programmatic Transaction Management with Examples. I hope you enjoy this Spring Framework Programmatic Transaction Management article.
About the Author: Pranaya Rout
Pranaya Rout has published more than 3,000 articles in his 11-year career. Pranaya Rout has very good experience with Microsoft Technologies, Including C#, VB, ASP.NET MVC, ASP.NET Web API, EF, EF Core, ADO.NET, LINQ, SQL Server, MYSQL, Oracle, ASP.NET Core, Cloud Computing, Microservices, Design Patterns and still learning new technologies.