事务的传播行为

事务的传播行为

如果当前存在一个事务事务的传播行为(Propagation Behavior)表示整个业务处理过程所跨越的业务对象,将以什么样的行为参与事务。即在一个 service 中调用其他的 service ,事务该怎么处理。

7个传播行为

在 spring 中,事务的隔离级别、传播行为、超时时间等都在接口 org.springframework.transaction.TransactionDefinotion 中定义。TransactionDefinotion 提供了以下几种传播行为:

传播行为 作用
PROPAGATION_REQUIRED 当前存在一个事务就加入,否则创建新事务
PROPAGATION_REQUIRES_NEW 不管是否存在,都创建新事务
PROPAGATION_SUPPORTS 存在就加入,否则直接执行方法
PROPAGATION_MANDATORY 强制要求存在一个事务,否则抛异常
PROPAGATION_NOT_SUPPORTED 不支持当前事务,如果存在就挂起
PROPAGATION_NEVER 永远不需要当前存在事务,否则抛异常
PROPAGATION_NESTED 存在就嵌套事务,不存在就新建

详细介绍:

  • PROPAGATION_REQUIRED :如果当前存在一个事务,则加入当前事务。如果不存在任何事务,则创建一个新的事务。

    PROPAGATION_REQUIRED 通常作为默认的事务传播行为

  • PROPAGATION_REQUIRES_NEW :不管当前是否存在事务,都会创建新的事务。如果存在事务,会将当前事务挂起。

    应用场景:某个业务对象所做的事情不想影响到外层事务时。例如,当前业务方法需要向数据库中更新日志信息,但即便这些日志信息更新失败,我们也不想因为改业务方法的事务回滚,而影响到外层事务的提交。

  • PROPAGATION_SUPPORTS :如果当前存在一个事务,则加入当前事务。如果不存在事务,就直接执行。

    对于一些查询方法来说,该传播行为比较适合。例如,A.Service() 会首先更新数据库,然后调用 B.Service() 进行查询,而如果 B.Service() 是 PROPAGATION_SUPPORTS 传播行为,就可以读取到 A.Service() 更新操作之后的最新结果。

  • PROPAGATION_MANDATORY :强制要求当前存在一个事务,如不存在就抛出异常。

    如果某个方法需要事务支持,但自身又不管理事务提交或回滚,就比较适合用这个传播行为。

  • PROPAGATION_NOT_SUPPORTED :不支持当前事务,而是在没有事务的情况下执行。如果当前存在事务,当前事务原则上将被挂起(Suspend)。

  • PROPAGATION_NEVER :永远不需要当前存在事务,如果存在当前事务,则抛出异常。

  • PROPAGATION_NESTED :如果存在当前事务,则在当前事务的一个嵌套事务中执行;如果不存在当前事务,则与 PROPAGATION_REQUIRED 的行为类似,创建新的事务,在新事务中执行。

超时时间和只读

TransactionDefinition 提供了 TIMEOUT_DEFAULT 常量定义,用来指定事务的超时时间。TIMEOUT_DEFAULT 默认值为-1,这会采用当前事务系统默认的超时时间。我们可以通过 TransactionDefinition 的具体实现类提供自定义的事务超时时间。

TransactionDefinition 提供的最后一个重要信息就是将要创建的是否是一个只读(ReadOnly)的事务。如果需要创建一个只读的事务的话,可以通过 TransactionDefinition 的相关实现类进行设置。只读的事务仅仅是给相应的 ResourceManager 提供一种优化的提示,但最终是否提供优化,则由具体的ResourceManager来决定。对于一些查询来说,我们通常会希望它们采用只读事务。

在代码中使用

我们现在都是用基于注解的声明式事务管理,也就是 @Transactional ,先来看看源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

可以看出在注解 @Transactional 中定义了默认的 传播行为、事务隔离级别、超时时间、只读标记、回滚设置等属性。

在实际的应用中,大多数情况下使用这些默认属性就可以满足我们的需求。而如果有特殊需要,也可以在注解中设置符合我们需要的属性值,如下列代码:

@Transactional
@Service
public class QuoteService implements IQuoteService{
  
  @Resource
  private JdbcTemplate jdbcTemplate;
  
  @Trangactional(propagationePropagation.SUPPORTS, readOnly=true, timeout=20)
  public Quote getQuate() {
    return (Quote)getJdbcTemplate().queryForObject("SELECT * FROM fx_quote wherequote_id=2", new RowMapper(){
      public Object mapRow(ResultSetrs, int row) throws SQLException {
        Quote quote = new Quotel;
        //...
        return qote;
      }});
   
  }
  
  @Transactional(propagation=Propagation.SUPPORTS, readOnly=true, timeout=20)
  public Quote getQuateByDaterime(Daterime daterime) {
    throw new NotImolementedException();
  }
  //...
}

public void saveQuote(Quote quote){
  // save...
}

如果将 @Transactional 标注为对象级别的话,对象中的方法将“继承”该对象级别上的 @Transactiona 的事务管理元数据信息。

如果某个方法有特殊的事务管理需求,可以在方法级别添加更加详细的 @Transactional 设定,比如getQuote*()方法。通过将相同的事务管理行为提取到对象级别的@Transactional,可以有效地减少标注的数量。

如果是在 springboot 项目中,需要在启动类上加入 @EnableTransactionManagement

@SpringBootApplication
@MapperScan("com.itle.credit.mapper")
@EnableTransactionManagement	//开启事务支持
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}
posted @ 2023-03-08 11:59  乐子不痞  阅读(123)  评论(0编辑  收藏  举报
回到顶部