spring的事务管理
引言: 在Spring中@Transactional提供一种控制事务管理的快捷手段,但是很多人都只是@Transactional简单使用,并未深入了解,其各个配置项的使用方法,本文将深入讲解各个配置项的使用。
1. @Transactional的定义
Spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题。在现实中,实际的问题往往比我们预期的要复杂很多,这就要求对@Transactional有深入的了解,以来应对复杂问题。
首先我们来看看@Transactional的代码定义:
- @Target({ElementType.METHOD, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Inherited
- @Documented
- public @interface Transactional {
- /**
- * A qualifier value for the specified transaction.
- * <p>May be used to determine the target transaction manager,
- * matching the qualifier value (or the bean name) of a specific
- * {@link org.springframework.transaction.PlatformTransactionManager}
- * bean definition.
- */
- String value() default "";
- /**
- * The transaction propagation type.
- * Defaults to {@link Propagation#REQUIRED}.
- * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
- */
- Propagation propagation() default Propagation.REQUIRED;
- /**
- * The transaction isolation level.
- * Defaults to {@link Isolation#DEFAULT}.
- * @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel()
- */
- Isolation isolation() default Isolation.DEFAULT;
- /**
- * The timeout for this transaction.
- * Defaults to the default timeout of the underlying transaction system.
- * @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
- */
- int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
- /**
- * {@code true} if the transaction is read-only.
- * Defaults to {@code false}.
- * <p>This just serves as a hint for the actual transaction subsystem;
- * it will <i>not necessarily</i> cause failure of write access attempts.
- * A transaction manager which cannot interpret the read-only hint will
- * <i>not</i> throw an exception when asked for a read-only transaction.
- * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()
- */
- boolean readOnly() default false;
- /**
- * Defines zero (0) or more exception {@link Class classes}, which must be a
- * subclass of {@link Throwable}, indicating which exception types must cause
- * a transaction rollback.
- * <p>This is the preferred way to construct a rollback rule, matching the
- * exception class and subclasses.
- * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}
- */
- Class<? extends Throwable>[] rollbackFor() default {};
- /**
- * Defines zero (0) or more exception names (for exceptions which must be a
- * subclass of {@link Throwable}), indicating which exception types must cause
- * a transaction rollback.
- * <p>This can be a substring, with no wildcard support at present.
- * A value of "ServletException" would match
- * {@link javax.servlet.ServletException} and subclasses, for example.
- * <p><b>NB: </b>Consider carefully how specific the pattern is, and whether
- * to include package information (which isn't mandatory). For example,
- * "Exception" will match nearly anything, and will probably hide other rules.
- * "java.lang.Exception" would be correct if "Exception" was meant to define
- * a rule for all checked exceptions. With more unusual {@link Exception}
- * names such as "BaseBusinessException" there is no need to use a FQN.
- * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}
- */
- String[] rollbackForClassName() default {};
- /**
- * Defines zero (0) or more exception {@link Class Classes}, which must be a
- * subclass of {@link Throwable}, indicating which exception types must <b>not</b>
- * cause a transaction rollback.
- * <p>This is the preferred way to construct a rollback rule, matching the
- * exception class and subclasses.
- * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}
- */
- Class<? extends Throwable>[] noRollbackFor() default {};
- /**
- * Defines zero (0) or more exception names (for exceptions which must be a
- * subclass of {@link Throwable}) indicating which exception types must <b>not</b>
- * cause a transaction rollback.
- * <p>See the description of {@link #rollbackForClassName()} for more info on how
- * the specified names are treated.
- * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}
- */
- String[] noRollbackForClassName() default {};
- }
2. 使用@Transactional的Spring配置
为了使用基于@Transactional的事务管理,需要在Spring中进行如下的配置:
- <beans:bean id="transactionManager"
- class="org.springframework.orm.jpa.JpaTransactionManager">
- <beans:property name="dataSource" ref="dataSource" />
- <beans:property name="entityManagerFactory" ref="entityManagerFactory" />
- </beans:bean>
- <!-- 声明使用注解式事务 -->
- <tx:annotation-driven transaction-manager="transactionManager" />
-
或者这样
<!-- 对数据源进行事务管理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
dataSource是在Spring配置文件中定义的数据源的对象实例,EntityManagerFactory是基于JPA使用的实体类管理器:org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean。这些都是用来配置与数据库的连接信息,本质上,@Transactional使用了JDBC的事务来进行事务控制的。
<annotation-driven>标签的声明,则是在Spring内部启用@Transactional来进行事务管理,类似开关之类的声明。
3. @Transactional之value
value这里主要用来指定不同的事务管理器;主要用来满足在同一个系统中,存在不同的事务管理器。比如在Spring中,声明了两种事务管理器txManager1, txManager2.
然后,用户可以根据这个参数来根据需要指定特定的txManager.
那有同学会问什么情况下会存在多个事务管理器的情况呢? 比如在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事务管理器的。
4. @Transactional之propagation
Propagation支持7种不同的传播机制:
- REQUIRED
- SUPPORTS:
- MANDATORY:
- REQUIRES_NEW
- NOT_SUPPORTED
- NEVER:
- NESTED:
如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保证点.内部事务回滚不会对外部事务造成影响, 它只对DataSourceTransactionManager 事务管理器起效.
其实大家最感到困惑的是REQUIRED_NEW和NESTED两种不同的传播机制,功能类似,都涉及到了事务嵌套的问题,那两者有何区别呢?该如何正确使用这两种模式呢?
事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
- TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务超时
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,timeout=5)
事务只读属性
“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。
因此,“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可
spring事务回滚规则
指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。
MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。
@Transactional属性
属性 | 类型 | 描述 |
---|---|---|
value | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | enum: Propagation | 可选的事务传播行为设置 |
isolation | enum: Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int (in seconds granularity) | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
用法
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
上一篇: VS Code的配置
推荐阅读