@Transactional注解式事务

@EnableTransactionManagement

正常情况下,需要我们的ApplicationConfiguration类加上@EnableTransactionManagement注解才能开启事务管理

Spring Data JPA 在 spring.factories 里面默认加载 TransactionAutoConfiguration类,默认采用AdviceMode.PROXY,所以默认情况的事务管理机制是代理方式

通过添加@Transactional注解式配置方法

@Transactional 源码

public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    // 在多个DataSource时指定Transaction
    @AliasFor("value")
    String transactionManager() default "";

    String[] label() default {};
    // 设置事务的传播行为
    Propagation propagation() default Propagation.REQUIRED;
    // 用于设置底层数据库的事务隔离级别,事务隔离级别多用于处理并发情况,一般使用默认的即可
    Isolation isolation() default Isolation.DEFAULT;
    // 设置事务超时的秒数,默认永不超时
    int timeout() default -1;

    String timeoutString() default "";
    // 当前事务是否为只读事务
    boolean readOnly() default false;
    // 设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚
    Class<? extends Throwable>[] rollbackFor() default {};
    // 用于设置需要进行混gun的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行回滚
    // @Transactional(rollbackForClassName = "RuntimeException")
    String[] rollbackForClassName() default {};
    // 不需要进行回滚的异常类数组,在方法中抛出数组中异常时不进行事务回滚
    Class<? extends Throwable>[] noRollbackFor() default {};
    // 设置不需要进行回滚的异常类名称数组,在方法中抛出数组中的类异常时不进行回滚
    String[] noRollbackForClassName() default {};
}

 

propagation: 传播行为

事务传播行为是指: 如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为

public enum Propagation {
    // 如果当前事务存在,则加入该事务;如果没有事务则创建一个新的事务
    REQUIRED(0),
    // 如果当前存在事务,则加入该事务; 如果没有事务,则以非事务的方式运行
    SUPPORTS(1),
    // 如果当前存在事务,则加入该事务; 如果没有事务则抛出异常
    MANDATORY(2),
    // 创建一个新事务,如果当前存在事务,则把事务挂起
    REQUIRES_NEW(3),
    // 以非事务方式运行,如果当前存在事务,则把当前事务挂起
    NOT_SUPPORTED(4),
    // 以非事务方式运行,如果当前存在事务,则抛出异常
    NEVER(5),
    // 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行,如果没有事务,则该值等价于REQUIRED
    NESTED(6);

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

 

事务隔离级别:

隔离级别是指若干个并发的事物之间的隔离程度,主要相关场景包括: 脏读取、重复读、幻读

public enum Isolation {
    // 默认值,表示使用底层数据库的默认隔离级别
    DEFAULT(-1),
    // 一个事务可以读取另一个事务修改但还没有提交的数据,该级别不能防止脏读和不可重复读,因此很少使用该隔离级别
    READ_UNCOMMITTED(1),
    // 该隔离级别表示一个事务只能读取另一个事务已经提交的数据,该级别可以防止脏读,这也是大读书情况下的推荐值
    READ_COMMITTED(2),
    // 该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同
    // 即使在多次查询之间又新增的数据满足该查询,这些新增的记录也会被忽略,该级别可以防止脏读和不可重复读
    REPEATABLE_READ(4),
    // 所有的事务一次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读、幻读
    // 但是浙江严重影响程序的性能,通常情况下也不会用到该级别
    SERIALIZABLE(8);

    private final int value;

    private Isolation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

@Transactional 注解可以设置在方法或者类上,一般都设置在方法上

 

需要注意的几点:

  • @Transactional 只能被应用到public方法上,兑取其他非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能
  • 用Spring事务管理器,由Spring来负责数据库的打开、提交、回滚,默认遇到RuntimeException会回滚,而遇到Exception不会回滚,这个可以在rollBackFor 上设置
  • @Transactional注解可以被应用于接口定义和接口方法、类定义和类public方法上,但是@Transactional仅仅是一种元数据,能够被识别
  • Spring团队建议是在具体的类或方法实现上使用@Transactional注解,而不要使用在高层级的类或方法上,注解是不能被继承的,如果正在使用基于类的代理时,那么事务的设置不能被基于类的代理所识别
  • 事务有两种配置方法,一种是显式的注解事务,在注解式事务下,不加service方法上是没有任何事务的,还有一种是隐式事务,AspectJ的思路配置方法

声明式事务

@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    public static final String transactionExecution = "execution (* com.banywl.study.studyspringdatajpa.dao.*.*(**))";

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor(){
        // 设置拦截哪些类
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(transactionExecution);
        // 配置 advisor
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        // 指定不同的方法使用不同的策略
        Properties properties = new Properties();
        properties.setProperty("get*","PROPAGATION_REQUIRED,-Exception");
        properties.setProperty("add*","PROPAGATION_REQUIRED,-Exception");
        properties.setProperty("save*","PROPAGATION_REQUIRED,-Exception");
        properties.setProperty("update*","PROPAGATION_REQUIRED,-Exception");
        properties.setProperty("delete*","PROPAGATION_REQUIRED,-Exception");
        // 创建并设置事务拦截器
        TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager,properties);
        advisor.setAdvice(txAdvice);
        return advisor;
    }
    
}