赞
踩
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 实质是使用了 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。
@Transactional注解支持10个属性的设置,这里只讲解其中使用较多的三个属性:propagation、isolation、rollbackFor 。其中propagation属性用来枚举事务的传播行为,isolation用来设置事务隔离级别,rollbackFor进行异常事务回滚。
模拟银行转账:用户1向用户2转500
准备数据
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
模拟正常转账
@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,在正常情况下是没有问题的,但是出现异常,就会发现,第一条数据已经发生了更改,第二条却没有,这就导致了数据不同步,会很不好。
模拟正常转账
@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,不管是正常情况,还是异常情况,都不会出现两边数据有异常
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。