【事务管理】 SpingBoot注解@Transactional

使用注解@Transactional可以在操作数据时出现异常便启动回滚(所有此次操作过的记录都回退),一般用在controller或者service层上。

注解放在类上,相当于给类下的所有方法都添加了事务,无论这个方法是不是public修饰的。

如果使用了@Transactional,就不要使用try/catch去捕捉异常,因为异常出现后,要让@Transactional去捕捉,然后才会发生回滚,否则被try/catch提前捕捉之后,@Transactional是捕捉不到的,这时候可以使用手动回滚

@Transactional 在捕捉异常之后,会进行回滚处理,然后异常依旧会向上抛

@Transactional只适用于单数据源的使用场景,如果发生回滚,只会对首个操作的数据库进行回滚

案例:

@Transactional(rollbackOn = Exception.class)
public void test() {
     try {
         //业务代码
         //主动捕捉异常导致框架无法捕获,从而导致事物失效
     } catch (Exception e) {
         // 手动回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
     }
   
 }

 Spring的事务管理是用AOP实现的,理解这点很重要

Spring底层使用了两种方式来实现动态代理,一种是Java自带的动态代理,另一种是CGLib。如果是使用JDK动态代理生成的代理对象,Debug可以看到 JdkDynamicAopProxy,而如果是CGLib生成的对象,可以看到是 EnhancerBySpringCGLIB 

那Spring具体是使用的哪种方式呢?网上有很多文章说,Spring默认产生代理对象的行为是:如果你的Bean有对应的接口,是使用的基于JDK的动态代理,否则是使用CGLIB。但这样说其实不准确,Spring用了下面这个配置来控制它,如果这个配置是false,才是上面我们说的这个逻辑。而如果这个配置是true,则所有的要使用AOP的Bean都使用CGLIB代理,不管它是不是有接口。而我们使用最新版的SpringBoot的话,这个值默认就是true。

spring.aop.proxy-target-class=true

所以现在如果使用SpringBoot的话,我们的AOP代理对象都是用CGLIB生成的

两者实现的原理不同,JDK动态代理是基于Java反射来实现的,而CGLIB动态代理是基于修改字节码,生成子类来实现的,底层是使用的asm开源库。

两者都有一些限制,JDK动态代理,Bean必须要有接口;CGLIB不能对final类或方法做代理。

该段文字引用于:关于Spring AOP的灵魂十问

--------------------------------------------------------------------------------------

事务的隔离级别

// 默认为 Isolation.DEFAULT
@Transactional(isolation=Isolation.DEFAULT)
public void test() {
        
}
枚举值定义
DEFAULT默认值,表示使用底层数据库的默认隔离级别。大部分数据库为READ_COMMITTED(MySql默认隔离级别为REPEATABLE)
READ_UNCOMMITTED该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。 
READ_COMMITTED该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。 
REPEATABLE_READ该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。 
SERIALIZABLE所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 

事务的传播级别 

事务的传播是指多个事务之间的关系

//默认值为 Propagation.REQUIRED
@Transactional(propagation=Propagation.REQUIRED)
public void test() {
        
}
枚举值定义
REQUIRED如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 
SUPPORTS如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 
REQUIRES_NEW创建一个新的事务,如果当前存在事务,则把当前事务挂起。 
NOT_SUPPORTED以非事务方式运行,如果当前存在事务,则把当前事务挂起。 
NEVER以非事务方式运行,如果当前存在事务,则抛出异常。 
NESTED如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED 。 

rollbackFor 参数

@Transactional的rollbackFor用于指定能够触发事务回滚的异常类型,可以指定多个,用逗号分隔。

rollbackFor默认值为UncheckedException,包括了RuntimeException和Error.

当我们直接使用@Transactional不指定rollbackFor时,Exception及其子类都不会触发回滚。

原文链接:https://blog.csdn.net/qq_43900956/article/details/120993392

注意事项:

一、带事务的方法调用非事务方法,那么非事务方法也会加入到同一个事务中

二、非事务方法内部调用事务方法,是不会触发事务的

@Transactional 底层使用的是aop,使用的代理对象才能使用事务,本方法内部调用,绕过了代理对象,所以事务无效。

 引用:Spring中同一类@Transactional修饰方法相互调用的坑_前路无畏的博客-CSDN博客

解决:

1、另外在抽象出一层,做事务层,不要把相互调用的方法放到一类中,然后以注入的方式调用

2强制使用 AspectJ 对方法进行切面

java注解@Transactional事务类内调用不生效问题及解决办法 - 云淡风轻博客 - 博客园

三、只有@Transactional 注解应用到 public 方法,才能进行事务管理。

四、异常被自己的 catch捕捉会导致@Transactional失效

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
@Transactional是Spring框架中用于配置事务的注解之一,它可以被应用于类或方法上。当被应用于类上时,该类中的所有公共方法都将被配置为支持事务。当被应用于方法上时,该方法将被配置为支持事务。@Transactional注解提供了多个属性,可以用于配置事务的各个方面,例如隔离级别、传播行为、只读状态等。以下是一个使用@Transactional注解配置事务的例子: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) public void addUser(User user) { userDao.addUser(user); } @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) public void updateUser(User user) { userDao.updateUser(user); } @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) public void deleteUser(int userId) { userDao.deleteUser(userId); } @Transactional(propagation = Propagation.SUPPORTS, isolation = Isolation.DEFAULT, readOnly = true) public User getUserById(int userId) { return userDao.getUserById(userId); } @Transactional(propagation = Propagation.SUPPORTS, isolation = Isolation.DEFAULT, readOnly = true) public List<User> getAllUsers() { return userDao.getAllUsers(); } } ``` 在上面的例子中,@Transactional注解被应用于UserServiceImpl类中的所有公共方法上。其中,propagation属性指定了事务的传播行为,isolation属性指定了事务的隔离级别,readOnly属性指定了事务是否只读。在这个例子中,事务的传播行为被配置为REQUIRED,表示如果当前没有事务,则创建一个新的事务;如果当前已经存在事务,则加入该事务。事务的隔离级别被配置为DEFAULT,表示使用数据库默认的隔离级别。事务的只读状态被配置为false,表示该事务可以读取和修改数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值