Backend Development 8 min read

Mastering Programmatic Transaction Management in Spring Boot 2.3.9

This guide explains Spring Boot's two programmatic transaction approaches—TransactionTemplate/TransactionalOperator and direct TransactionManager—provides detailed code examples for using TransactionTemplate with return values, rollbacks, property configuration, demonstrates PlatformTransactionManager usage, and shows how to listen to transaction events with @TransactionalEventListener.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Programmatic Transaction Management in Spring Boot 2.3.9

Environment

springboot2.3.9.RELEASE

Programmatic Transaction Management Methods

Spring provides two programmatic transaction management methods:

Use

TransactionTemplate

or

TransactionalOperator

Directly create a

TransactionManager

implementation

Spring officially recommends the

TransactionTemplate

approach.

Preparation

<code>// Entity class
@Entity
@Table(name = "BC_USERS")
@Data
public class Users {
    private String username;
    private String password;
    private Integer status = 0;
}

// DAO
public interface UsersRepository extends JpaRepository<Users, String> {
    @Modifying
    @Query("update Users u set u.status=?1,u.password='123123' where u.id=?2")
    int updateUsers(Integer status, String id);
}

@Mapper
public interface UsersMapper {
    int insertUser(Users user);
}

<!-- Mapper.xml -->
<insert id="insertUser" parameterType="com.pack.domain.Users">
  insert into bc_users (id, username, password) values (#{id}, #{username}, #{password})
</insert>
</code>

TransactionTemplate

1.1 With Return Value

<code>@Service
public class UserService {
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private UsersRepository usersRepository;

    public Integer saveUsers(Users users) {
        this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
            @Override
            public Integer doInTransaction(TransactionStatus status) {
                return usersMapper.insertUser(users);
            }
        });
        return result;
    }
}
</code>

1.2 Without Return Value

<code>public void saveUsers(Users users) {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            usersMapper.insertUser(users);
        }
    });
}
</code>

1.3 Transaction Rollback

<code>public Users saveUser(Users users) {
    return transactionTemplate.execute(new TransactionCallback<Users>() {
        @Override
        public Users doInTransaction(TransactionStatus status) {
            try {
                return usersMapper.insertUser(users);
            } catch (Exception e) {
                status.setRollbackOnly();
            }
            return null;
        }
    });
}
</code>

1.4 Configuring TransactionTemplate Properties

<code>private TransactionTemplate transactionTemplate;

public UserService(PlatformTransactionManager transactionManager) {
    this.transactionTemplate = new TransactionTemplate(transactionManager);
    this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
    this.transactionTemplate.setTimeout(30); // seconds
}
</code>

TransactionalOperator

TransactionalOperator is suitable for reactive programming; it is not covered here.

TransactionManager

Two types of transaction managers are available:

PlatformTransactionManager

ReactiveTransactionManager (for reactive programming, not covered)

PlatformTransactionManager Example

<code>private PlatformTransactionManager transactionManager;
private DefaultTransactionDefinition definition;
private TransactionStatus status;
@Resource
private UsersRepository usersRepository;

public UserService3(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
    definition = new DefaultTransactionDefinition();
    definition.setName("pgName");
    definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
}

public Integer saveUsers(Users users) {
    TransactionStatus status = this.transactionManager.getTransaction(definition);
    Integer result = null;
    try {
        result = usersMapper.insertUser(users);
    } catch (Exception e) {
        transactionManager.rollback(status);
        throw e;
    }
    transactionManager.commit(status);
    publisher.publishEvent(new UsersEvent(users));
    return result;
}
</code>

Transaction Event Listening

Use

@TransactionalEventListener

to listen to events at different transaction phases. This annotation works only with declarative (thread‑bound) transactions managed by

PlatformTransactionManager

.

<code>@Component
public class TxListenerComponent {
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleUsersAfterCommit(UsersEvent usersEvent) {
        Users user = (Users) usersEvent.getSource();
        System.out.println("AfterCommit收到事件通知:" + user.getPassword());
    }
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
    public void handleUsersAfterCompletion(UsersEvent usersEvent) {
        Users user = (Users) usersEvent.getSource();
        System.out.println("AfterCompletion收到事件通知:" + user.getPassword());
    }
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void handleUsersAfterRollback(UsersEvent usersEvent) {
        Users user = (Users) usersEvent.getSource();
        System.out.println("AfterRollback收到事件通知:" + user.getPassword());
    }
    @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
    public void handleUsersBeforeCommit(UsersEvent usersEvent) {
        Users user = (Users) usersEvent.getSource();
        System.out.println("BeforeCommit收到事件通知:" + user.getPassword());
    }
}

@Resource
private ApplicationEventPublisher publisher;
@Resource
private UsersMapper usersMapper;

public Integer saveUsers(Users users) {
    Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
        @Override
        public Integer doInTransaction(TransactionStatus status) {
            return usersMapper.insertUser(users);
        }
    });
    publisher.publishEvent(new UsersEvent(users));
    return result;
}
</code>

Conclusion

Programmatic transaction management is suitable for a small number of transactional operations, such as when a service performs extensive calculations before committing once. For scenarios with many transactional steps, declarative transaction management is more appropriate.

JavaBackend DevelopmentSpring Boottransaction managementProgrammatic Transactions
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.