SpringBoot - 优雅的处理【长事务】

简介: 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);
}


相关文章
|
设计模式 前端开发 关系型数据库
【DDD】全网最详细2万字讲解DDD,从理论到实战(代码示例) 3
【DDD】全网最详细2万字讲解DDD,从理论到实战(代码示例)
5505 2
|
XML JSON Java
SpringBoot 实战:在 RequestBody 中优雅的使用枚举参数
本文先上实战,说一下如何实现。在 优雅的使用枚举参数 代码的基础上,我们继续实现。如果想要获取源码,可以关注公号「看山的小屋」,回复 spring 即可。
1612 0
SpringBoot 实战:在 RequestBody 中优雅的使用枚举参数
|
设计模式 前端开发 Java
DTO和VO的区别及使用场景详解
DTO和VO的区别及使用场景详解
7317 1
|
Java 关系型数据库 MySQL
SpringBoot手动提交事务
SpringBoot手动提交事务
1503 0
|
SQL 缓存 安全
深入解析MyBatis-Plus LambdaQueryWrapper与QueryWrapper:高效数据查询的秘密
深入解析MyBatis-Plus LambdaQueryWrapper与QueryWrapper:高效数据查询的秘密
13997 2
|
Java 数据库连接 mybatis
mybatis注解@Select中添加判断条件<script>
mybatis注解@Select中添加判断条件<script>
1138 0
|
SQL
Mybatis-plus 自定义SQL注入器查询@TableLogic 逻辑删除后的数据
Mybatis-plus使用@TableLogic注解进行逻辑删除数据后,在某些场景下,又需要查询该数据时,又不想写SQL。 自定义Mybatis-plus的SQL注入器一劳永逸的解决该问题
1256 0
|
弹性计算 关系型数据库 数据库
PostgreSQL 数据库实例只读锁定(readonly) - 硬锁定,软锁定,解锁
标签 PostgreSQL , 只读 , 锁定 , readonly , recovery.conf , 恢复模式 , pg_is_in_revoery , default_transaction_read_only 背景 在一些场景中,可能要将数据库设置为只读模式。 例如, 1、云数据库,当使用的容量超过了购买的限制时。切换到只读(锁定)模式,确保用户不会用超。 2、业务上需要对
7619 0
|
缓存 NoSQL Java
Springboot 大事务问题的常用优化方案
Springboot 大事务问题的常用优化方案
|
缓存 安全 前端开发
来聊聊Java项目分层规范
本文讨论了Java项目的分层规范,强调了分层的重要性以避免代码不易扩展和职责边界模糊。作者分享了阿里提出的六层分层模型(开放接口层、终端显示层、Web层、Service层、Manager层、Mapper层)以及对应的领域模型(DO、DTO、VO、query)。同时,提出了简化版的分层规约,以提高开发效率。作者是CSDN Java博客专家,维护者之一的Java Guide项目,并提供了个人项目结构示例。文章鼓励读者关注其公众号以获取更多交流机会。
3337 4