欢迎关注公众号【sharedCode】致力于主流中间件的源码分析, 我的网站:https://www.shared-code.com/java
1.spring事务实现方式及原理
Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring 是没法提供事务功能的。真正的数据库层的事务提交和回滚是在binlog提交以后进行提交的 经过 redo log 来重作, undo log来回滚。spring
通常咱们在程序里面使用的都是在方法上面加@Transactional
注解,这种属于声明式事务。数据库
声明式事务本质是经过 AOP 功能,对方法先后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始以前加入一个事务,在执行完目标方法以后根据执行状况提交或者回滚事务。源码分析
2.数据库自己不支持事务
这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操做的,InnoDB 才是支持事务的引擎,通常要支持事务都会使用 InnoDB网站
3.当前类的调用
@Service public class UserServiceImpl implements UserService { public void update(User user) { updateUser(user); } @Transactional(rollbackFor = Exception.class) public void updateUser(User user) { // update user } }
上面的这种状况下是不会有事务管理操做的。this
经过看声明式事务的原理可知,spring使用的是AOP切面的方式,本质上使用的是动态代理来达到事务管理的目的,当前类调用的方法上面加@Transactional
这个是没有任何做用的,由于调用这个方法的是this
..net
OK, 咱们在看下面的一种例子。代理
@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
新开启一个事务,那么新开的事务管用么?code
答案是:无论用!中间件
由于它们发生了自身调用,就调该类本身的方法,而没有通过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。
4.方法不是public的
@Service public class UserServiceImpl implements UserService { @Transactional(rollbackFor = Exception.class) private void updateUser(User user) { // update user } }
private
方法是不会被spring代理的,所以是不会有事务产生的,这种作法是无效的。
5.没有被spring管理
//@Service public class UserServiceImpl implements UserService { @Transactional(rollbackFor = Exception.class) public void updateUser(User user) { // update user } }
没有被spring管理的bean, spring连代理对象都没法生成,固然无效咯。
6.配置的事务传播性有问题
@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这三种时,就有可能存在事务不生效
7.异常被你 "抓住"了
@Service public class UserServiceImpl implements UserService { @Transactional(rollbackFor = Exception.class) public void update(User user) { try{ // update user }catch(Execption e){ log.error("异常",e) } } }
异常被抓了,这样子代理类就没办法知道你到底有没有错误,需不须要回滚,因此这种状况也是没办法回滚的哦。
8.接口层声明式事务使用cglib代理
public interface UserService { @Transactional(rollbackFor = Exception.class) public void update(User user) }
@Service public class UserServiceImpl implements UserService { public void update(User user) { // update user } }
经过元素的 "proxy-target-class" 属性值来控制是基于接口的仍是基于类的代理被建立。若是 "proxy-target-class" 属值被设置为 "true",那么基于类的代理将起做用(这时须要CGLIB库cglib.jar在CLASSPATH中)。若是 "proxy-target-class" 属值被设置为 "false" 或者这个属性被省略,那么标准的JDK基于接口的代理将起做用
注解@Transactional cglib与java动态代理最大区别是代理目标对象不用实现接口,那么注解要是写到接口方法上,要是使用cglib代理,这是注解事务就失效了,为了保持兼容注解最好都写到实现类方法上。
9.rollbackFor异常指定错误
@Service public class UserServiceImpl implements UserService { @Transactional public void update(User user) { // update user } }
上面这种没有指定回滚异常,这个时候默认的回滚异常是RuntimeException
,若是出现其余异常那么就不会回滚事务