《Spring Boot 3实战案例合集》现已囊括超过70篇精选实战文章,并且此合集承诺将永久持续更新,为您带来最前沿的技术资讯与实践经验。欢迎积极订阅,享受不断升级的知识盛宴!订阅用户将特别获赠合集内所有文章的最终版MD文档(详尽学习笔记),以及完整的项目源码,助您在学习道路上畅通无阻。
环境:SpringBoot2.7.18
1. 编程方式与声明方式
声明式工作中用的最多,因为太过方便,你甚至都不需要做任何的配置。
@Transactionalpublic void save() {// todo}
编程方式,用的相对较少,因为比起声明式要麻烦点(写的代码多了);但是如果能结合适当的场景那么这种编程的方式会给你系统代理性能的提升。
@Resourceprivate TransactionTemplate template ;public void save() {// todotemplate.execute(new TransactionCallback<Object>() {public Object doInTransaction(TransactionStatus status) {// todo}} ;// todo}
上面两种对事务的使用方式非常明显大家一般都不会选择使用编程的方式。
编程事务应用场景
细粒度控制:编程式事务提供了对事务的细粒度控制。它允许开发人员在代码中明确地定义事务的开始、提交和回滚,从而可以精确地控制事务的边界和行为。这对于需要精确控制事务逻辑的场景非常有用,例如在复杂的业务逻辑中,可能需要根据不同的条件来决定是否提交或回滚事务。
非标准事务管理:当事务管理逻辑不符合标准的事务模型时,编程式事务是一个很好的选择。例如,在某些特殊情况下,可能需要在一个方法中执行多个数据库操作,并且这些操作需要被划分到不同的事务中。在这种情况下,如果使用了声明式事务只能控制一个数据源,没法对多个数据源进行控制。这里只是举例,这种情况属于分布式事务了,应该考虑如何保证事务的一致性了。
声明式事务应用场景
简化事务管理:声明式事务通过注解定义事务规则,使得事务管理变得简化。它不需要在业务逻辑代码中显式地编写事务管理的代码,从而减少了代码的复杂性。
标准事务管理:声明式事务通常用于标准的事务管理场景,例如数据库的增删改查操作。它提供了对事务的自动管理,包括自动提交和回滚事务,从而减少了开发人员对事务管理的关注。
2. 编程式事务应用
Spring提供了2中编程式的事务管理方式:
使用
TransactionTemplate或 TransactionalOperator通过 TransactionManager
注意:TransactionTemplate是在命令式中使用,TransactionalOperator是在反应式中使用。
2.1 TransactionTemplate
在Spring Boot中如果你引入了如:data-jpa或者data-jdbc、data-r2dbc相关的依赖后,系统会自动的为我们配置TransactionTemplate或TransactionalOperator
源码如下
public TransactionalOperator transactionalOperator(ReactiveTransactionManager transactionManager) {return TransactionalOperator.create(transactionManager);}public static class TransactionTemplateConfiguration {public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {return new TransactionTemplate(transactionManager);}}
TransactionTemplate完整使用示例
@Resourceprivate TransactionTemplate template ;@Resourceprivate JdbcTemplate jdbcTemplate ;public void save(Person person) {template.execute(new TransactionCallback<Object>() {@Overridepublic Object doInTransaction(TransactionStatus status) {try {int result = jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(), person.getName());} catch (Exception e) {e.printStackTrace() ;// 当发生异常后设置为回滚status.setRollbackOnly() ;}return "success"}}) ;}
上面示例是有返回值的情况,如果你不需要返回值则可以将TransactionCallback替换为TransactionCallbackWithoutResult 。
属性配置
public class PersonService {private final TransactionTemplate transactionTemplate ;public PersonService(PlatformTransactionManager transactionManager) {this.transactionTemplate = new TransactionTemplate(transactionManager);// 设置事务的隔离级别this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);// 设置事务超时时间this.transactionTemplate.setTimeout(30); // 30 seconds}}
注意:你最好不要使用Spring容器自动配置的TransactionTemplate来进行相关属性的配置,因为这是全局的,所有的操作都将使用这一份配置。
2.2 TransactionalOperator
方式1:
public class UserService {private R2dbcEntityTemplate template ;private final TransactionalOperator transactionalOperator;public UserService(ReactiveTransactionManager transactionManager) {this.transactionalOperator = TransactionalOperator.create(transactionManager);}public Mono<User> save(User user) {return Mono.just(user).then(template.insert(user)).doOnNext(u -> {// 人为的制造异常System.out.println(1 / 0) ;}).as(transactionalOperator::transactional);}}
在一个事务的上下文中运行。上面代码执行后你将在控制台看到事务回滚信息。

如果没有上面的as(transactionalOperator::transactional)操作,那么数据将会被正常的插入到数据库中。
方式2:
public Flux<Integer> save2(User user) {return this.transactionalOperator.execute(new TransactionCallback<Integer>() {public Mono<Integer> doInTransaction(ReactiveTransaction status) {return Mono.just(user).then(template.insert(user)).doOnNext(u -> {System.out.println(1 / 0) ;}).doOnError(RuntimeException.class, e -> status.setRollbackOnly()).map(User::getUid) ;}}) ;}
2.3 TransactionManager
public void save() {Person person = new Person();person.setAge(36);person.setName("张三");DefaultTransactionDefinition definition = new DefaultTransactionDefinition();definition.setName("CustomTx") ;definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED) ;definition.setReadOnly(false) ;definition.setTimeout(2) ;TransactionStatus transactionStatus = tm.getTransaction(definition) ;try {jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(), person.getName());// 制造异常System.out.println(1 / 0) ;// 提交tm.commit(transactionStatus) ;} catch (Exception e) {e.printStackTrace() ;// 回滚tm.rollback(transactionStatus) ;}}
3. 性能对比(错误的应用事务)
数据库连接配置

为了看到更好的效果,这里只配置了5个连接。
3.1 基于注解方式
业务方法
public void save(Person person) {try {// 模拟针对Person执行其它非事务耗时操作TimeUnit.SECONDS.sleep(1) ;} catch (InterruptedException e) {}this.personRepository.saveAndFlush(person) ;}
通过jmeter测试
线程池配置

测试结果

吞吐量非常低,并且还出现了错误。该错误是由于在30s内没有获取到数据库连接。

3.2 基于编程方式
业务方法
public void save(Person person) {try {// 模拟针对Person执行其它非事务耗时操作TimeUnit.SECONDS.sleep(1) ;} catch (InterruptedException e) {}this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {protected void doInTransactionWithoutResult(TransactionStatus status) {personRepository.saveAndFlush(person) ;}}) ;}
jmeter配置不变,测试结果

吞吐量大幅提升,并且没有出现错误情况。
从上面的测试结果能够充分的说明合理的使用事务方式在有些场景下是能够非常明显提升系统的整体性能。
总结:非事务性的操作应该拿到事务外执行,要么选择编程事务。
以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏
推荐文章
Spring Boot 通过@JsonComponent注解完全控制JSON数据
强大!基于 Spring Boot 通过2种模板技术,多种方式生成PDF文件
@Transactional用错!严重影响性能,Spring Boot 事务最佳实践
Jackson才是王!SpringBoot优雅的控制JSON数据




