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

// 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>

TransactionTemplate

1.1 With Return Value

@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;
    }
}

1.2 Without Return Value

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

1.3 Transaction Rollback

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;
        }
    });
}

1.4 Configuring TransactionTemplate Properties

private TransactionTemplate transactionTemplate;

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

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

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;
}

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.

@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;
}

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

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

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.