spring事物失效的原因
1 spring事物实现方式及原理
Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring 是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog 或者 redo log 实现的。
一般我们在程序里面使用的都是在方法上面加@Transactional 注解,这种属于声明式事物。
声明式事务本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
2 失效原因
1.1 数据库本身不支持事物
这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。
2.2 方法不是public的
注解@Transactional只能放在public修饰的方法上才起作用(private 方法是不会被spring代理的,因此是不会有事物产生的,这种做法是无效的)。
2.3 没有被 Spring 管理
没有被spring管理的bean, spring连代理对象都无法生成,事务自然无效。
2.4 当前类的调用
@Service
public class UserServiceImpl implements UserService {
public void update(User user) {
updateUser(user);
}
@Transactional(rollbackFor = Exception.class)
public void updateUser(User user) {
// update user
}
}
上面的这种情况下是不会有事物管理操作的。
通过看声明式事物的原理可知,spring使用的是AOP切面的方式,本质上使用的是动态代理来达到事物管理的目的,当前类调用的方法上面加@Transactional 这个是没有任何作用的,因为调用这个方法的是this。
我们再看下面的一种例子:
@Service
public class UserServiceImpl implements UserService {
@Transactional(rollbackFor = Exception.class)
public void update(User user) {
updateUser(user);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUser(User user) {
// update user
}
}
这次在 update 方法上加了 @Transactional,updateUser 加了 REQUIRES_NEW 新开启一个事务,那么新开的事务管用么?
答案是:不管用!
因为它们发生了自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。
2.5 配置的事物传播性有问题
@Service
public class UserServiceImpl implements UserService {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void update(User user) {
// update user
}
}
回顾一下spring的事物传播行为
Spring 事务的传播行为说的是,当多个事务同时存在的时候, Spring 如何处理这些事务的行为。
类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 创建新事务,无论当前存不存在事务,都创建新事务。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按 REQUIRED 属性执行。 |
当传播行为设置了PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER,PROPAGATION_SUPPORTS这三种时,就有可能存在事物不生效。
2.6 异常被你 "抓住"了
@Service
public class UserServiceImpl implements UserService {
@Transactional(rollbackFor = Exception.class)
public void update(User user) {
try{
// update user
}catch(Execption e){
log.error("异常",e)
}
}
}
异常被抓了,这样子代理类就没办法知道你到底有没有错误,需不需要回滚,所以这种情况也是没办法回滚的哦。
2.7 rollbackFor异常指定错误
@Service
public class UserServiceImpl implements UserService {
@Transactional
public void update(User user) {
// update user
}
}
上面这种没有指定回滚异常,这个时候默认的回滚异常是RuntimeException ,如果出现其他异常那么就不会回滚事物。