@Transactional
package org.springframework.transaction.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.TransactionDefinition;
/**
* Describes a transaction attribute on an individual method or on a class.
*
* <p>At the class level, this annotation applies as a default to all methods of
* the declaring class and its subclasses. Note that it does not apply to ancestor
* classes up the class hierarchy; methods need to be locally redeclared in order
* to participate in a subclass-level annotation.
*
* <p>This annotation type is generally directly comparable to Spring's
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
* class, and in fact {@link AnnotationTransactionAttributeSource} will directly
* convert the data to the latter class, so that Spring's transaction support code
* does not have to know about annotations. If no rules are relevant to the exception,
* it will be treated like
* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute}
* (rolling back on {@link RuntimeException} and {@link Error} but not on checked
* exceptions).
*
* <p>For specific information about the semantics of this annotation's attributes,
* consult the {@link org.springframework.transaction.TransactionDefinition} and
* {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
*
* @author Colin Sampaleanu
* @author Juergen Hoeller
* @author Sam Brannen
* @since 1.2
* @see org.springframework.transaction.interceptor.TransactionAttribute
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute
* @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
/**
* Alias for {@link #transactionManager}.
* @see #transactionManager
*/
@AliasFor("transactionManager")
String value() default "";
/**
* A <em>qualifier</em> 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.
* @since 4.2
* @see #value
*/
@AliasFor("value")
String transactionManager() default "";
/**
* The transaction propagation type.
* <p>Defaults to {@link Propagation#REQUIRED}.
* @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* The transaction isolation level.
* <p>Defaults to {@link Isolation#DEFAULT}.
* <p>Exclusively designed for use with {@link Propagation#REQUIRED} or
* {@link Propagation#REQUIRES_NEW} since it only applies to newly started
* transactions. Consider switching the "validateExistingTransactions" flag to
* "true" on your transaction manager if you'd like isolation level declarations
* to get rejected when participating in an existing transaction with a different
* isolation level.
* @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel()
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* The timeout for this transaction (in seconds).
* <p>Defaults to the default timeout of the underlying transaction system.
* <p>Exclusively designed for use with {@link Propagation#REQUIRED} or
* {@link Propagation#REQUIRES_NEW} since it only applies to newly started
* transactions.
* @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/**
* A boolean flag that can be set to {@code true} if the transaction is
* effectively read-only, allowing for corresponding optimizations at runtime.
* <p>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
* but rather silently ignore the hint.
* @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly()
*/
boolean readOnly() default false;
/**
* Defines zero (0) or more exception {@link Class classes}, which must be
* subclasses of {@link Throwable}, indicating which exception types must cause
* a transaction rollback.
* <p>By default, a transaction will be rolling back on {@link RuntimeException}
* and {@link Error} but not on checked exceptions (business exceptions). See
* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)}
* for a detailed explanation.
* <p>This is the preferred way to construct a rollback rule (in contrast to
* {@link #rollbackForClassName}), matching the exception class and its subclasses.
* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}.
* @see #rollbackForClassName
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
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 of a fully qualified class name, with no wildcard
* support at present. For example, a value of {@code "ServletException"} would
* match {@code javax.servlet.ServletException} and its subclasses.
* <p><b>NB:</b> Consider carefully how specific the pattern is and whether
* to include package information (which isn't mandatory). For example,
* {@code "Exception"} will match nearly anything and will probably hide other
* rules. {@code "java.lang.Exception"} would be correct if {@code "Exception"}
* were meant to define a rule for all checked exceptions. With more unusual
* {@link Exception} names such as {@code "BaseBusinessException"} there is no
* need to use a FQN.
* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}.
* @see #rollbackFor
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
String[] rollbackForClassName() default {};
/**
* Defines zero (0) or more exception {@link Class Classes}, which must be
* subclasses 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 (in contrast
* to {@link #noRollbackForClassName}), matching the exception class and
* its subclasses.
* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}.
* @see #noRollbackForClassName
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
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 further
* information on how the specified names are treated.
* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}.
* @see #noRollbackFor
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
String[] noRollbackForClassName() default {};
}
属性 | 类型 | 描述 |
---|---|---|
value | String | 可选的限定描述符,指定使用的事务管理器 |
transactionManager | String | 可选的限定描述符,指定事务的限定符值 |
propagation | enum:Propagation | 可选的事务传播行为设置 |
isolation | enum:isolation | 可选的事务隔离级别设置 |
timeout | int (in seconds granularity) | 事务超时时间设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
添加位置
1)接口实现类或接口实现方法上,而不是接口类中。
2)访问权限:public 的方法才起作用。@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。
系统设计:将标签放置在需要进行事务管理的方法上,而不是放在所有接口实现类上:只读的接口就不需要事务管理,由于配置了@Transactional就需要AOP拦截及事务的处理,可能影响系统性能。
@Transactional 实现原理
@Transactional 实质是使用了 JDBC 的事务来进行事务控制的
@Transactional 基于 Spring 的动态代理的机制
@Transactional 实现原理:
事务开始时,通过AOP机制,生成一个代理connection对象,
并将其放入 DataSource 实例的某个与 DataSourceTransactionManager 相关的某处容器中。
在接下来的整个事务中,客户代码都应该使用该 connection 连接数据库,
执行所有数据库命令。
[不使用该 connection 连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚]
(物理连接 connection 逻辑上新建一个会话session;
DataSource 与 TransactionManager 配置相同的数据源)事务结束时,回滚在第1步骤中得到的代理 connection 对象上执行的数据库命令,
然后关闭该代理 connection 对象。
(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)
事务的隔离级别
是指若干个并发的事务之间的隔离程度
- @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读,
不可重复读) 基本不使用- @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)
- @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)
- @Transactional(isolation = Isolation.SERIALIZABLE):串行化
事务传播行为
如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为
- TransactionDefinition.PROPAGATION_REQUIRED:
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。- TransactionDefinition.PROPAGATION_REQUIRES_NEW:
创建一个新的事务,如果当前存在事务,则把当前事务挂起。- TransactionDefinition.PROPAGATION_SUPPORTS:
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
以非事务方式运行,如果当前存在事务,则把当前事务挂起。- TransactionDefinition.PROPAGATION_NEVER:
以非事务方式运行,如果当前存在事务,则抛出异常。- TransactionDefinition.PROPAGATION_MANDATORY:
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。- TransactionDefinition.PROPAGATION_NESTED:
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;
如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
spring事务回滚规则
- 指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常,spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务
- 默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常不会导致事务回滚。
- 用spring事务管理器,由spring来负责数据库的打开、提交、回滚、默认遇到运行期例外(throw new RuntimeException(“注释”))会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new RuntimeException(“注释”))不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上@Transactional(rollbackFor={Exception.class,其他异常}),如果让unchecked例外不回滚;@Transactional(notRollbackFor=RunTimeException.class)
演示示例
@Transactional注解支持10个属性的设置,这里只讲解其中使用较多的三个属性:propagation、isolation、rollbackFor 。其中propagation属性用来枚举事务的传播行为,isolation用来设置事务隔离级别,rollbackFor进行异常事务回滚。
模拟银行转账:用户1向用户2转500
准备数据
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
- 没有使用@Transactional
模拟正常转账
@Service
public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements TestUserService {
@Override
public void testUser(TestUser testUser) throws CoBusinessException {
// 更新用户1,减500
TestUser one = getById(1);
one.setMoney(500);
updateById(one);
// 查询用户2的金额是否超过最大值,是抛异常,否正常通过
TestUser two = getById(2);
if (two.getMoney() > 1400) {
throw new RuntimeException("金额过大");
}
// 更新用户2的余额
two.setMoney(1500);
updateById(two);
}
}
数据库表的变化
- 变化前
- 变化后
模拟转账异常
public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements TestUserService {
@Override
public void testUser(TestUser testUser) throws CoBusinessException {
// 更新用户1,减500
TestUser one = getById(1);
one.setMoney(500);
updateById(one);
// 查询用户2的金额是否超过最大值,是抛异常,否正常通过
TestUser two = getById(2);
if (two.getMoney() > 400) {
throw new RuntimeException("金额过大");
}
// 更新用户2的余额
two.setMoney(1500);
updateById(two);
}
}
数据库表的变化
- 变化前
- 变化后
总结:可以看出,没有加@Transactional,在正常情况下是没有问题的,但是出现异常,就会发现,第一条数据已经发生了更改,第二条却没有,这就导致了数据不同步,会很不好。
- 使用了@Transactional
模拟正常转账
@Service
public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements TestUserService {
@Override
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public void testUser(TestUser testUser) throws CoBusinessException {
// 更新用户1,减500
TestUser one = getById(1);
one.setMoney(500);
updateById(one);
// 查询用户2的金额是否超过最大值,是抛异常,否正常通过
TestUser two = getById(2);
if (two.getMoney() > 1400) {
throw new RuntimeException("金额过大");
}
// 更新用户2的余额
two.setMoney(1500);
updateById(two);
}
}
数据库表的变化
- 变化前
- 变化后
模拟转账异常
public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements TestUserService {
@Override
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public void testUser(TestUser testUser) throws CoBusinessException {
// 更新用户1,减500
TestUser one = getById(1);
one.setMoney(500);
updateById(one);
// 查询用户2的金额是否超过最大值,是抛异常,否正常通过
TestUser two = getById(2);
if (two.getMoney() > 400) {
throw new RuntimeException("金额过大");
}
// 更新用户2的余额
two.setMoney(1500);
updateById(two);
}
}
数据库表的变化
- 变化前
- 变化后
总结:可以看出,加上了@Transactional,不管是正常情况,还是异常情况,都不会出现两边数据有异常