一、先简单介绍一下Spring事务的传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,
此时有若干选项可以指定一个事务性方法的执行行为。
在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,
则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,
如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,
则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,
如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,
如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,
则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,
则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,
则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
二、Spring事务的回滚机制
Spring的AOP即声明式事务管理默认是针对unchecked exception回滚。
Spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行
commit or rollback(Spring默认取决于是否抛出runtimeException)。
如果你在方法中有try{}catch(Exception e){}处理,那么try里面的代码块就脱离了事务的管理,
若要事务生效需要在catch中throw new RuntimeException ("xxxxxx");
补充:简单介绍一下@Transactional注解底层实现方式吧,毫无疑问,是通过动态代理,
那么动态代理又分为JDK自身和CGLIB,这个也不多赘述了,
(一)
第一点要注意的就是在@Transactional注解的方法中,再调用本类中的其他方法method2时,
那么method2方法上的@Transactional注解是不!会!生!效!的!
但是加上也并不会报错,拿图片简单帮助理解一下吧
通过代理对象在目标对象前后进行方法增强,也就是事务的开启提交和回滚。
那么继续调用本类中其他方法是怎样呢,如下图

可见目标对象内部的自我调用,也就是通过this.指向的目标对象将不会执行方法的增强。
先说第二点需要注意的地方,等下说如何解决上面第一点的问题。
(二)
第二点就是@Transactional注解的方法必须是公共方法,就是必须是public修饰符!!!
至于这个的原因,发表下个人的理解吧,因为JVM的动态代理是基于接口实现的,
通过代理类将目标方法进行增强,想一下也是啦,没有权限访问那么你让我怎么进行,,,好吧,
这个我也没有深入研究底层,个人理解个人理解。
在这里我也放个问题吧,希望有高手可以回复指点指点我,因为JVM动态代理是基于接口实现的,
那么是不是service层都要按照接口和实现类的开发模式,注解才会生效呢,
就是说controller层直接调用没有接口的service层,加了注解也一样不起作用吧,这个懒了,
没有测试,其一是因为没有人会这么开发吧,其二是我就认为是不起作用的,哈哈,,,
三
下面来解决一下第一点的问题,如何在方法中调用本类中其他方法呢。
通过AopContext.currentProxy ()获取到本类的代理对象,再去调用就好啦。
因为这个是CGLIB实现,所以要开启AOP,当然也很简单,
在springboot启动类上加上注解@EnableAspectJAutoProxy(exposeProxy = true)就可以啦,
这个依赖大家自行搜一下就好啦。要注意,注意,代理对象调用的方法也要是public修饰符,
否则方法中获取不到注入的bean,会报空指针错误。
(一)一个类中调用另一个方法事务一致性
package com.po.service; import com.po.dao.UserMapper; import com.po.entity.UserPo; import com.sun.org.apache.bcel.internal.generic.NEW; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; UserPo userPo1 = new UserPo(); UserPo userPo2 = new UserPo(); UserPo userPo3 = new UserPo(); UserPo userPo4 = new UserPo(); @Override @Transactional public void updateUser( ) { this.updateUser1(); this.updateUser2(); } public void updateUser1(){ userPo1.setId(24); userPo1.setUsername("lanpo1"); userMapper.updateUser(userPo1); // userPo2.setId(25); userPo2.setUsername("lanpo2"); userMapper.updateUser(userPo2); } public void updateUser2(){ userPo3.setId(27); userPo3.setUsername("lanpo3"); userMapper.updateUser(userPo3); int i=1/0;//异常 userPo4.setId(28); userPo4.setUsername("lanpo4"); userMapper.updateUser(userPo4); } }
dao
import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper { UserPo getUser(int id); void updateUser(UserPo userPo); }
mapper.xml
<update id="updateUser" parameterType="com.po.entity.UserPo"> UPDATE USER SET username=#{username} WHERE id =#{id} </update>
entity
private Integer id; private String username; private String birthday; private String sex;
updateUser方法在一个事务中,更新失败

(二)Transactioanl失效的情况
package com.po.service; import com.po.dao.UserMapper; import com.po.entity.UserPo; import com.sun.org.apache.bcel.internal.generic.NEW; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; UserPo userPo1 = new UserPo(); UserPo userPo2 = new UserPo(); UserPo userPo3 = new UserPo(); UserPo userPo4 = new UserPo(); @Override @Transactional public void updateUser( ) { this.updateUser1(); this.updateUser2(); } @Transactional public void updateUser1(){ userPo1.setId(24); userPo1.setUsername("lanpo1"); userMapper.updateUser(userPo1); // userPo2.setId(25); userPo2.setUsername("lanpo2"); userMapper.updateUser(userPo2); } @Transactional public void updateUser2(){ userPo3.setId(27); userPo3.setUsername("lanpo3"); userMapper.updateUser(userPo3); int i=1/0;//异常 userPo4.setId(28); userPo4.setUsername("lanpo4"); userMapper.updateUser(userPo4); } }

更新失败,可见在同类中被调用的方法加@Transactional无效
(三)
package com.po.service; import com.po.dao.UserMapper; import com.po.entity.UserPo; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; UserPo userPo1 = new UserPo(); UserPo userPo2 = new UserPo(); UserPo userPo3 = new UserPo(); UserPo userPo4 = new UserPo(); @Override @Transactional public void updateUser( ) { UserServiceImpl currentProxy = (UserServiceImpl) AopContext.currentProxy(); currentProxy.updateUser1(); currentProxy.updateUser2(); } public void updateUser1(){ userPo1.setId(24); userPo1.setUsername("lanpo1"); userMapper.updateUser(userPo1); // userPo2.setId(25); userPo2.setUsername("lanpo2"); userMapper.updateUser(userPo2); } public void updateUser2(){ userPo3.setId(27); userPo3.setUsername("lanpo3"); userMapper.updateUser(userPo3); int i=1/0;//异常 userPo4.setId(28); userPo4.setUsername("lanpo4"); userMapper.updateUser(userPo4); } }

更新失败。
上面第一和第三情况不管使不使用代理调用方法1和方法2,方法 updateUser()
都处在一个事务中,
四条更新操作全部失败。
那么有人可能会有疑问了,在方法1和方法2上都加@Transactional注解呢?
答案是结果和上面是一致的。
小结:只要方法updateUser上有注解,并且方法1和方法2都处于当前事务中
(不使用代理调用,方法1和方法2上的@Transactional注解是不生效的;使用代理,
需要方法1和方法2都处在updateUser方法的事务中,默认或者嵌套事务均可,
当然也可以不加@Transactional注解),那么整体保持事务一致性。
四、要方法1和方法2均单独保持事务一致性怎么办呢(保留疑问)
如果不是用代理调用@Transactional注解是不生效的,
所以一定要使用代理调用实现,然后让方法1和方法2分别单独开启新的事务,
便OK啦。下面摆上图片。
(一)
@Override public void updateUser( ) { UserServiceImpl currentProxy = (UserServiceImpl) AopContext.currentProxy(); currentProxy.updateUser1(); currentProxy.updateUser2(); } @Transactional public void updateUser1(){ userPo1.setId(24); userPo1.setUsername("lanpo1"); userMapper.updateUser(userPo1); // userPo2.setId(25); userPo2.setUsername("lanpo2"); userMapper.updateUser(userPo2); } @Transactional public void updateUser2(){ userPo3.setId(27); userPo3.setUsername("lanpo3"); userMapper.updateUser(userPo3); int i=1/0;//异常 userPo4.setId(28); userPo4.setUsername("lanpo4"); userMapper.updateUser(userPo4); }
五、通过将方法1放到另一个类达到单独保持事务一致问题
(一)查看将方法1放到另一个类达到事务统一
package com.po.service; import com.po.dao.UserMapper; import com.po.entity.UserPo; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; @Autowired UserServiceImpl2 userServiceImpl2; UserPo userPo1 = new UserPo(); UserPo userPo2 = new UserPo(); UserPo userPo3 = new UserPo(); UserPo userPo4 = new UserPo(); @Override @Transactional public void updateUser( ) { userServiceImpl2.updateUser1(); this.updateUser2(); } @Override public void updateUser2(){ userPo3.setId(27); userPo3.setUsername("lanpo3"); userMapper.updateUser(userPo3); int i=1/0;//异常 userPo4.setId(28); userPo4.setUsername("lanpo4"); userMapper.updateUser(userPo4); } }
UserServiceImpl2
package com.po.service; import com.po.dao.UserMapper; import com.po.entity.UserPo; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserServiceImpl2 { @Autowired UserMapper userMapper; UserPo userPo1 = new UserPo(); UserPo userPo2 = new UserPo(); UserPo userPo3 = new UserPo(); UserPo userPo4 = new UserPo(); public void updateUser1(){ userPo1.setId(24); userPo1.setUsername("lanpo1"); userMapper.updateUser(userPo1); // userPo2.setId(25); userPo2.setUsername("lanpo2"); userMapper.updateUser(userPo2); } }

方法一方法二事务一致。
五、
@Transactiona 放在类上相当于该类的每一个方法都加上
@Transactiona ,而且方法上的会覆盖类上的。
package com.po.service; import com.po.dao.UserMapper; import com.po.entity.UserPo; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service @Transactional public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; @Autowired UserServiceImpl2 userServiceImpl2; UserPo userPo1 = new UserPo(); UserPo userPo2 = new UserPo(); UserPo userPo3 = new UserPo(); UserPo userPo4 = new UserPo(); @Override public void updateUser( ) { this.updateUser1(); this.updateUser2(); } @Override public void updateUser1(){ userPo1.setId(24); userPo1.setUsername("lanpo1"); userMapper.updateUser(userPo1); // userPo2.setId(25); userPo2.setUsername("lanpo2"); userMapper.updateUser(userPo2); } @Override public void updateUser2(){ userPo3.setId(27); userPo3.setUsername("lanpo3"); userMapper.updateUser(userPo3); int i=1/0;//异常 userPo4.setId(28); userPo4.setUsername("lanpo4"); userMapper.updateUser(userPo4); } }
六、 @Transactional和try catch的一起使用注意
package com.po.service; import com.po.dao.UserMapper; import com.po.entity.UserPo; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; @Autowired UserServiceImpl2 userServiceImpl2; UserPo userPo1 = new UserPo(); UserPo userPo2 = new UserPo(); UserPo userPo3 = new UserPo(); UserPo userPo4 = new UserPo(); @Override @Transactional public void updateUser( ) { this.updateUser2(); } @Override public void updateUser2(){ try { userPo3.setId(27); userPo3.setUsername("lanpo3"); userMapper.updateUser(userPo3); int i=1/0;//异常 }catch (Exception e){ userPo4.setId(28); userPo4.setUsername("lanpo4"); userMapper.updateUser(userPo4); } } }

发现:虽然有异常但是没有回滚
(二)
package com.po.service; import com.po.dao.UserMapper; import com.po.entity.UserPo; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; @Autowired UserServiceImpl2 userServiceImpl2; UserPo userPo1 = new UserPo(); UserPo userPo2 = new UserPo(); UserPo userPo3 = new UserPo(); UserPo userPo4 = new UserPo(); @Override @Transactional public void updateUser( ) { this.updateUser2(); } @Override public void updateUser2(){ try { userPo3.setId(27); userPo3.setUsername("lanpo3"); userMapper.updateUser(userPo3); int i=1/0;//异常 }catch (Exception e){ userPo4.setId(28); userPo4.setUsername("lanpo4"); userMapper.updateUser(userPo4); /* 注意:1.try代码会脱离Spring的事务,必须要在catch中抛出 运行时异常才能保证事务一致性 2. 56-58行代码也是处在同一事务中的 */ throw new RuntimeException("事务异常"); } } }


完毕。
浙公网安备 33010602011771号