事务的传播行为
事务的传播行为
如果当前存在一个事务事务的传播行为(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);
}
}