事务是逻辑上的一组操作,要么都执行,要么都不执行。
Spring 对事务的支持取决于数据库 ,如果是使用Mysql的myisam引擎,那么就是从根本上就不支持事务。Spring事务的本质就是数据库对事务的支持,使用JDBC的事务管理机制,利用java.sql.Connection对象完成对事务的提交。
未使用Spring框架前,Java中事务实现示例代码如下:

  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. public class DemoApplication {
  4. public static void main(String[] args) {
  5. // 1.获取连接
  6. Connection conn = DriverManager.getConnection();
  7. try {
  8. // 2.将自动提交设置为false
  9. conn.setAutoCommit(false);
  10. /*-----------------*/
  11. // 3.执行一到多个CRUD操作
  12. /*-----------------*/
  13. // 4.1手动提交
  14. conn.commit();
  15. } catch (Exception e) {
  16. // 4.2一旦其中一个操作出错都将回滚,所有操作都不成功
  17. conn.rollback();
  18. } finally {
  19. // 5.关闭连接
  20. conn.colse();
  21. }
  22. }
  23. }

Spring框架则提供统一的事务抽象,无论是JTA、JDBC、Hibernate/JPA、Mybatis/Mybatis-Plus,Spring都使用统一的编程模型,使得应用程序可以很容易地在不同的事务框架之间进行切换。
Spring事务管理的核心接口是PlatformTransactionManager。接口PlatformTransactionManager定义事务操作的行为,PlatformTransactionManager依赖TransactionDefinitionTransactionStatus接口。

  1. // 设置事务的传播行为以及隔离级别
  2. @Transactional(propagation = Propagation.NESTED, isolation = Isolation.READ_COMMITTED)
  3. public int upsert() {
  4. ....
  5. }

注解属性

  1. @Target({ElementType.METHOD, ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Inherited
  4. @Documented
  5. public @interface Transactional {
  6. @AliasFor("transactionManager")
  7. String value() default "";
  8. @AliasFor("value")
  9. String transactionManager() default "";
  10. Propagation propagation() default Propagation.REQUIRED;
  11. Isolation isolation() default Isolation.DEFAULT;
  12. int timeout() default -1;
  13. boolean readOnly() default false;
  14. Class<? extends Throwable>[] rollbackFor() default {};
  15. String[] rollbackForClassName() default {};
  16. Class<? extends Throwable>[] noRollbackFor() default {};
  17. String[] noRollbackForClassName() default {};
  18. }
字段名 类型 含义
value String value 主要用来指定不同的事务管理器,满足在同一个系统中,存在不同的事务管理器。如果在 Spring 中,配置了多个数据源声明了多个事务管理器,可以通过该参数来进行指定事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

事务传播机制 【propagation】

  1. public interface TransactionDefinition {
  2. /**
  3. * 默认选择,如果当前方法没有事务就新建一个事务;如果已存在一个事务,就加入到这个事务中
  4. */
  5. int PROPAGATION_REQUIRED = 0;
  6. /**
  7. * 如果当前方法存在事务,则加入到这个事务中,如果当前不存在事务,以非事务方式执行
  8. */
  9. int PROPAGATION_SUPPORTS = 1;
  10. /**
  11. * 被修饰的方法必须在事务中,否则抛出异常
  12. */
  13. int PROPAGATION_MANDATORY = 2;
  14. /**
  15. * 必须运行在自己的事务中,如果当前方法已经在事务中,挂起当前事务,新建一个
  16. */
  17. int PROPAGATION_REQUIRES_NEW = 3;
  18. /**
  19. * 被修饰的方法不应该运行在事务中,如果存在当前事务,则该事务在方法运行期间被挂起
  20. */
  21. int PROPAGATION_NOT_SUPPORTED = 4;
  22. /**
  23. * 被修饰的方法必须不在事务中,否则抛出异常
  24. */
  25. int PROPAGATION_NEVER = 5;
  26. /**
  27. * 当前方法支持嵌套事务,嵌套事务与当前事务进行单独提交和回滚,如果不存在当前事务,则行为和Required一样
  28. */
  29. int PROPAGATION_NESTED = 6;
  30. }

事务隔离级别【isolation】

同样是在TransctionDefinition类中定义的。

  1. public interface TransactionDefinition {
  2. /**
  3. * 使用数据库本身的隔离级别,MySQL(RR)、Oracle(RC)
  4. */
  5. int ISOLATION_DEFAULT = -1;
  6. /**
  7. * 读未提交
  8. */
  9. int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
  10. /**
  11. * 读已提交
  12. */
  13. int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
  14. /**
  15. * 可重复度
  16. */
  17. int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
  18. /**
  19. * 串行化
  20. */
  21. int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
  22. }

只读模式【readOnly】

readOnly=true的意义是什么?为什么我一个数据查询操作还要启用事务支持呢?
目的仅是优化数据库执行,mysql默认对每一个连接都启用autocommit模式,该模式下每发送一条sql执行完成后就自动提交事务,并开启一个事务。当readOnly=true时,所有的sql查询就都在一个事务里完成了。

@Transactional 事务注解原理

@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。

  1. @Override
  2. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  3. if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
  4. Class<?> targetClass = config.getTargetClass();
  5. if (targetClass == null) {
  6. throw new AopConfigException("TargetSource cannot determine target class: " +
  7. "Either an interface or a target is required for proxy creation.");
  8. }
  9. if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
  10. return new JdkDynamicAopProxy(config);
  11. }
  12. return new ObjenesisCglibAopProxy(config);
  13. }
  14. else {
  15. return new JdkDynamicAopProxy(config);
  16. }
  17. }

@Transcational事务失效

  1. 数据库引擎不支持事务
  2. 没有被Spring管理
  3. 方法不是public
  4. 自调用
  5. 数据源没有配置事务管理器
  6. 传播机制设置成不支持事务(@Transactional(propagation = Propagation.NOT_SUPPORTED))
  7. 异常不抛出,内部捕获
  8. 配置的回滚异常类型和抛出的异常类型不匹配

2. 没有被Spring管理

  1. // 把@Service注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。
  2. // @Service
  3. public class OrderServiceImpl implements OrderService {
  4. @Transactional
  5. public void updateOrder(Order order) {
  6. // update order
  7. }
  8. }

3. 方法不是 public 的

源码解析:https://blog.csdn.net/CPLASF_/article/details/115365665

4. Spring AOP 自调用问题

原因
Spring中会产生两个类,原类A和代理类AA,各自有方法M1、M2和PM1、PM2,假设(P)M2上有注解,方法增强,(P)M1调用(P)M2。当外部直接调用代理类AA的PM2时候,是方法增强的(preHandler-M2-postHandler),但如果外部调用AA的PM1,实际是调用M1方法,M1在AA中并没有增强,只是直接调用A的M1方法,M1再调用M2方法(原类A方法的M2并没有被增强),所以没有走到增强逻辑,所以事务失效。

示例
若同一类中的其他没有 @Transactional 注解的方法内部调用有 @Transactional 注解的方法,有@Transactional 注解的方法的事务会失效。
这是由于Spring AOP代理的原因造成的,因为只有当 @Transactional 注解的方法在类以外被调用的时候,Spring 事务管理才生效。
MyService 类中的method1()调用method2()就会导致method2()的事务失效。

  1. @Service
  2. public class MyService {
  3. private void method1() {
  4. method2();
  5. //......
  6. }
  7. @Transactional
  8. public void method2() {
  9. //......
  10. }
  11. }

解决办法就是避免同一类中自调用或者使用 AspectJ 取代 Spring AOP 代理。

8. 配置的回滚异常类型和抛出的异常类型不匹配

  1. // 这样事务也是不生效的,因为默认回滚的是:RuntimeException
  2. // 如果你想触发其他异常的回滚,需要在注解上配置一下 @Transactional(rollbackFor = Exception.class)
  3. @Service
  4. public class OrderServiceImpl implements OrderService {
  5. @Transactional
  6. public void updateOrder(Order order) {
  7. try {
  8. // update order
  9. } catch {
  10. throw new Exception("更新错误");
  11. }
  12. }
  13. }

参考

【1】:https://javaguide.cn/system-design/framework/spring/spring-transaction.html#%E4%BA%8B%E5%8A%A1%E5%B1%9E%E6%80%A7%E8%AF%A6%E8%A7%A3