Spring Programmatic Transaction Management: A Detailed Tutorial
This article provides a comprehensive, step‑by‑step guide to using programmatic transactions in Spring, covering both PlatformTransactionManager and TransactionTemplate approaches, complete with Maven configuration, SQL setup, test code, and best‑practice usage patterns.
Article Overview
This article aims to thoroughly explain how to use transactions in Spring, focusing on programmatic transaction management.
Content
Detailed explanation of Spring's programmatic transaction usage.
Two Ways to Use Transactions in Spring
Spring makes transaction handling easy, mainly offering two approaches:
Programmatic Transaction : Hard‑coded style.
Declarative Transaction : The familiar @Transactional annotation.
Programmatic Transaction
What Is a Programmatic Transaction?
It uses hard‑coded calls to Spring's transaction‑related classes to control transactions.
Two Main Uses of Programmatic Transactions
Method 1: Controlling transactions via PlatformTransactionManager .
Method 2: Controlling transactions via TransactionTemplate .
Method 1: PlatformTransactionManager
This is the most primitive way, with relatively large code volume; other methods are wrappers around it.
SQL Preparation
DROP DATABASE IF EXISTS javacode2018;
CREATE DATABASE IF NOT EXISTS javacode2018;
USE javacode2018;
DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user(
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(256) NOT NULL DEFAULT '' COMMENT '姓名'
);Maven Configuration
org.springframework
spring-jdbc
5.2.3.RELEASE
org.springframework
spring-tx
5.2.3.RELEASETest Code (PlatformTransactionManager)
The test demonstrates using JdbcTemplate together with a transaction manager.
@Test
public void test1() throws Exception {
// Define a data source
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
dataSource.setUsername("root");
dataSource.setPassword("root123");
dataSource.setInitialSize(5);
// JdbcTemplate for DB operations
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 1. Define transaction manager
PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
// 2. Define transaction attributes
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
// 3. Open transaction
TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
try {
System.out.println("before:" + jdbcTemplate.queryForList("SELECT * from t_user"));
jdbcTemplate.update("insert into t_user (name) values (?)", "test1-1");
jdbcTemplate.update("insert into t_user (name) values (?)", "test1-2");
// 5. Commit transaction
platformTransactionManager.commit(transactionStatus);
} catch (Exception e) {
// 6. Rollback transaction
platformTransactionManager.rollback(transactionStatus);
}
System.out.println("after:" + jdbcTemplate.queryForList("SELECT * from t_user"));
}Execution Output
before:[]
after:[{id=1, name=test1-1}, {id=2, name=test1-2}]Code Analysis
The code follows five steps:
Step 1: Define Transaction Manager – PlatformTransactionManager acts as the administrator that can start, commit, or roll back a transaction.
Spring provides several implementations, e.g., JpaTransactionManager , DataSourceTransactionManager , HibernateTransactionManager , and JtaTransactionManager . In this example, DataSourceTransactionManager is used because JdbcTemplate operates on a plain data source.
PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);Step 2: Define Transaction Attributes – Using TransactionDefinition (commonly DefaultTransactionDefinition ) to set isolation level, timeout, propagation behavior, etc.
Step 3: Open Transaction – Call platformTransactionManager.getTransaction(transactionDefinition) to obtain a TransactionStatus object.
TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);Step 4: Execute Business Logic – Perform DB operations via JdbcTemplate . Because the same DataSource is used, JdbcTemplate picks up the connection stored in a ThreadLocal, thus participating in the Spring transaction.
Step 5: Commit or Rollback
// Commit
platformTransactionManager.commit(transactionStatus);
// Rollback
platformTransactionManager.rollback(transactionStatus);Method 2: TransactionTemplate
TransactionTemplate wraps the boilerplate of opening, committing, and rolling back a transaction, providing two convenient methods.
Test Code (TransactionTemplate)
@Test
public void test1() throws Exception {
// Data source configuration (same as above)
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
dataSource.setUsername("root");
dataSource.setPassword("root123");
dataSource.setInitialSize(5);
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setTimeout(10); // 10 seconds timeout
TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager, transactionDefinition);
transactionTemplate.executeWithoutResult(status -> {
jdbcTemplate.update("insert into t_user (name) values (?)", "transactionTemplate-1");
jdbcTemplate.update("insert into t_user (name) values (?)", "transactionTemplate-2");
});
System.out.println("after:" + jdbcTemplate.queryForList("SELECT * from t_user"));
}Execution Output
after:[{id=1, name=transactionTemplate-1}, {id=2, name=transactionTemplate-2}]TransactionTemplate Methods
executeWithoutResult : No return value; receives a Consumer<TransactionStatus> where business logic is placed.
execute : Returns a value; receives a TransactionCallback<T> where doInTransaction contains the business logic.
Both methods automatically commit the transaction if no exception occurs and setRollbackOnly() is not called; otherwise they roll back.
Correct Usage Pattern for Programmatic Transactions
Register a PlatformTransactionManager and a TransactionTemplate as Spring beans and reuse them in services.
package com.javacode2018.tx.demo3;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
@Configuration
@ComponentScan
public class MainConfig3 {
@Bean
public DataSource dataSource() {
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
ds.setUsername("root");
ds.setPassword("root123");
ds.setInitialSize(5);
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
return new TransactionTemplate(transactionManager);
}
}Service example that combines two business methods into a single transaction:
package com.javacode2018.tx.demo3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.List;
@Component
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private TransactionTemplate transactionTemplate;
// Business method 1
public void bus1() {
transactionTemplate.executeWithoutResult(status -> {
jdbcTemplate.update("delete from t_user");
bus2();
});
}
// Business method 2
public void bus2() {
transactionTemplate.executeWithoutResult(status -> {
jdbcTemplate.update("insert into t_user (name) VALUE (?)", "java");
jdbcTemplate.update("insert into t_user (name) VALUE (?)", "spring");
jdbcTemplate.update("insert into t_user (name) VALUE (?)", "mybatis");
});
}
// Query all rows
public List userList() {
return jdbcTemplate.queryForList("select * from t_user");
}
}Test case showing the combined transaction:
package com.javacode2018.tx.demo3;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo3Test {
@Test
public void test1() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);
UserService userService = context.getBean(UserService.class);
userService.bus1();
System.out.println(userService.userList());
}
}Running test1() prints:
[{id=18, name=java}, {id=19, name=spring}, {id=20, name=mybatis}]If any exception occurs or transactionStatus.setRollbackOnly() is invoked inside bus1 or bus2 , the whole transaction will be rolled back.
Conclusion
Although programmatic transaction code looks complex, it reveals how Spring controls transactions under the hood. Most developers prefer declarative transactions, which are built on the same mechanisms but provide a simpler annotation‑based API.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.