SpringBoot - 优雅的处理长事务
Posted 小小工匠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot - 优雅的处理长事务相关的知识,希望对你有一定的参考价值。
文章目录
Pre
SpringBoot - 实践阿里巴巴【Manager 层_通用业务处理层】
在Spring体系中,在方法上加上注解@Transactional,Spring自动帮我们进行事务的开启、提交、回滚操作,真的是太方便了,以至于不分青红皂白,啥都搞上…
What
运行时间比较长,长时间未提交的事务,都可以称之为长事务
长事务引发的常见危害有:
- 数据库连接池被占满,应用无法获取连接资源
- 容易引发数据库死锁
- 数据库回滚时间长
- 在主从架构中会导致主从延时变大 等等
How
原则: 对事务方法进行拆分,尽量让事务变小,变快,减小事务的颗粒度
我们知道@Transactional注解进行事务管理的操作叫声明式事务, 使用声明式事务的优点 很明显,简单,仅需要关注业务, Spring框架自动帮我们进行事务的开启、提交以及回滚等操作。
声明式事务有一个最大的缺点,就是事务的颗粒度是整个方法,无法进行精细化控制。
那 与声明式事务对应的就是编程式事务 是不是可以解决 颗粒度的问题呢?
方法一 编程式事务
基于底层的API,开发者在代码中手动的管理事务的开启、提交、回滚等操作。
在spring项目中我们可以使用TransactionTemplate类的对象,手动控制事务。
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(ArtisanDto artisanDto)
transactionTemplate.execute(transactionStatus ->
artisanDao.save(artisanDto);
//....
// .....
return Boolean.TRUE;
);
使用编程式事务最大的好处就是可以精细化控制事务范围, 所以避免长事务最简单的方法就是不要使用声明式事务@Transactional,而是使用编程式事务手动控制事务范围。
使用@Transactional 又能避免产生长事务
那既想使用@Transactional 又想避免产生长事务呢?
那就需要对方法进行拆分,将不需要事务管理的逻辑与事务操作分开.
@Service
public class ArtisanService
public void create(ArtisanDto dto)
queryData();
biz();
save(dto);
//事务操作
@Transactional(rollbackFor = Throwable.class)
public void save(ArtisanDto dto)
artisanDao.insert(dto);
queryData()与biz()不需要事务,我们将其与事务方法save()拆开.
这种拆分会命中使用@Transactional注解时事务不生效的经典场景. @Transactional注解的声明式事务是通过spring aop起作用的,而spring aop需要生成代理对象,直接在同一个类中方法调用使用的还是原始对象,事务不生效。
其他几个常见的事务不生效的场景为:
- @Transactional 应用在非 public 修饰的方法上
- @Transactional 注解属性 propagation 设置错误
- @Transactional 注解属性 rollbackFor 设置错误
- 同一个类中方法调用,导致@Transactional失效
- 异常被catch捕获导致@Transactional失效
每日一博 - 常见的Spring事务失效&事务不回滚案例集锦
所以正确的拆分方法应该是下面两种
方法一
可以将方法放入另一个类,如新增 manager层,通过spring注入,这样符合了在对象之间调用的条件。
@Service
public class ArtisanService
@Autowired
private ArtisanManager artisanManager;
public void create(ArtisanDto dto)
queryData();
biz();
artisanManager.save(dto);
@Service
public class ArtisanManager
@Autowired
private ArtisanDao artisanDao;
@Transactional(rollbackFor = Throwable.class)
public void save(ArtisanCreateDTO dto)
artisanDao.saveData(dto);
参考 SpringBoot - 实践阿里巴巴【Manager 层_通用业务处理层】
方法二
启动类添加@EnableAspectJAutoProxy(exposeProxy = true)
,方法内使用AopContext.currentProxy()
获得代理类,使用事务。
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class SpringBootApplication
public void createArtisan(ArtisanCreateDTO dto)
ArtisanService artisanService = (ArtisanService)AopContext.currentProxy();
artisanService.saveData(dto);
以上是关于SpringBoot - 优雅的处理长事务的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot系列优雅的处理统一异常处理与统一结果返回
优雅写代码系统springboot+mybatis+pagehelper+mybatisplus+druid教你如何优雅写代码