【Java】@Transactional注解

参考文章:Spring——事务注解@Transactional,by 七海健人

@Transactional 来源于 Spring,用来管理事务。该注解属于 Spring 的声明式事务,还有一种是编程式事务,编程式对代码具有侵入性,不推荐使用。

注解的使用

可以用在接口、类、方法:

  • 接口:不推荐,Spring AOP 使用 CGLib 时,注解会失效
  • 类:生效于类中所有 public 方法
  • 方法:类和方法同时配置,方法注解会覆盖类注解

注解属性


public @interface Transactional {
 
    /**
     * 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。
     */
    @AliasFor("transactionManager")
    String value() default "";
 
    /**
     * 同上。
     */
    @AliasFor("value")
    String transactionManager() default "";
 
    /**
     * 事务的传播行为,默认值为 REQUIRED。
     */
    Propagation propagation() default Propagation.REQUIRED;
 
    /**
     * 事务的隔离规则,默认值采用 DEFAULT。
     */
    Isolation isolation() default Isolation.DEFAULT;
 
    /**
     * 事务超时时间。
     */
    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
 
    /**
     * 是否只读事务
     */
    boolean readOnly() default false;
 
    /**
     * 用于指定能够触发事务回滚的异常类型。
     */
    Class<? extends Throwable>[] rollbackFor() default {};
 
    /**
     * 同上,指定类名。
     */
    String[] rollbackForClassName() default {};
 
    /**
     * 用于指定不会触发事务回滚的异常类型
     */
    Class<? extends Throwable>[] noRollbackFor() default {};
 
    /**
     * 同上,指定类名
     */
    String[] noRollbackForClassName() default {};
 
}

失效的场景

  • 注解在非 public 方法上
  • 注解在 final 方法上
  • 同一个类中,某方法调用事务方法
    1. 解决办法一:事务方法新写一个 Service,然后类中注入使用
    2. 解决方法二:当前类中,注入自己,参考Spring——循环依赖&三级缓存
    3. 解决办法三:使用AopContext.currentProxy()获取当前类的代理对象
@Servcie
public class ServiceA {
 
   public void save(User user) {
         queryData1();
         queryData2();
         ((ServiceA)AopContext.currentProxy()).doSave(user);
   }
 
   @Transactional(rollbackFor=Exception.class)
   public void doSave(User user) {
       addData1();
       updateData2();
    }
 }
  • 事务方法所在类,没有被 Spring 管理,例如没有加@Service注解
  • 数据库存储引擎不支持事务:mysql 的 myisam 不支持事务
  • 两个方法不在同一线程中,即是两个不同的事务

Spring 中的传播方式和隔离级别

七种传播方式👇

// 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值
1. Propagation.REQUIRED

// 创建一个新的事务,如果当前存在事务,则把当前事务挂起
2. Propagation.REQUIRES_NEW

// 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
3. Propagation.SUPPORTS

// 以非事务方式运行,如果当前存在事务,则把当前事务挂起
4. Propagation.NOT_SUPPORTED

// 以非事务方式运行,如果当前存在事务,则抛出异常
5. Propagation.NEVER

// 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
6. Propagation.MANDATORY

/*
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行
如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
*/
7. Propagation.NESTED

五种隔离级别👇

// 使用后端数据库默认的隔离级别 对于MYSQL来说就是可重复读
1. Isolation.DEFAULT

// 是最低的隔离级别,允许读取尚未提交的数据变更(会出现脏读,不可重复读),基本不使用
2. Isolation.READ_UNCOMMITTED

// 允许读取并发事务已经提交的数据(会出现不可重复读和幻读)
3. Isolation.READ_COMMITTED

// 事物开启后,对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改(会出现幻读)
4. Isolation.REPEATABLE_READ

// 最高的隔离级别,完全服从ACID的隔离级别,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
5. Isolation.SERIALIZABLE
posted @ 2023-02-18 20:38  洗衣机o  阅读(259)  评论(0)    收藏  举报