引言: 在Spring中@Transactional提供一种控制事务管理的快捷手段,可是不少人都只是@Transactional简单使用,并未深刻了解,其各个配置项的使用方法,本文将深刻讲解各个配置项的使用。java
Spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题。在现实中,实际的问题每每比咱们预期的要复杂不少,这就要求对@Transactional有深刻的了解,以来应对复杂问题。spring
首先咱们来看看@Transactional的源代码定义:数据库
org.springframework.transaction.annotation.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 {};
}
复制代码
基于源代码,咱们能够发如今@Transactional中,原来有这么多的属性能够进行配置,从而达到复杂应用控制的目的。具体各个属性的用法和做用,将在下面逐一进行讲解和说明。并发
参数名称 | 功能描述app
-------------|---------------ide
readOnly |该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)测试
rollbackFor|该属性用于设置须要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})ui
rollbackForClassName|该属性用于设置须要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException")指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"})this
noRollbackFor|该属性用于设置不须要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName|该属性用于设置不须要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException")指定多个异常类名称:@Transactional(noRollbackForClassName={"RuntimeException","Exception"})
propagation|该属性用于设置事务的传播行为,具体取值可参考表6-7。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation|该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的状况,一般使用数据库的默认隔离级别便可,基本不须要进行设置
timeout|该属性用于设置事务的超时秒数,默认值为-1表示永不超时
事物传播行为介绍:
@Transactional(propagation=Propagation.REQUIRED) :若是有事务, 那么加入事务, 没有的话新建一个(默认状况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) :不论是否存在事务,都建立一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) :必须在一个已有的事务中执行,不然抛出异常
@Transactional(propagation=Propagation.NEVER) :必须在一个没有的事务中执行,不然抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) :若是其余bean调用这个方法,在其余bean中声明事务,那就用事务.若是其余bean没有声明事务,那就不用事务.
@Transactional(propagation=Propagation.NESTED) : 若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则进行与PROPAGATION_REQUIRED相似的操做。
前六个策略相似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。 它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)
复制代码
事物超时设置:
@Transactional(timeout=30) //默认是30秒
事务隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE):串行化
MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED
脏读 : 一个事务读取到另外一事务未提交的更新数据
不可重复读 : 在同一事务中, 屡次读取同一数据返回的结果有所不一样, 换句话说,
后续读取能够读到另外一事务已提交的更新数据.
可重复读:在同一事务中屡次
读取数据时, 可以保证所读数据同样, 也就是后续读取不能读到另外一事务已提交的更新数据
复制代码
幻读 : 一个事务读到另外一个事务已提交的insert数据
注意的几点:
一、@Transactional 只能被应用到public方法上, 对于其它非public的方法,若是标记了@Transactional也不会报错,但方法没有事务功能.
二、用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到须要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需咱们指定方式来让事务回滚要想全部异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .若是让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
以下:
1 @Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
2 public void methodName() {
3    throw new Exception("注释");
4 }
5 @Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚
6 public ItimDaoImpl getItemDaoImpl() {
7    throw new RuntimeException("注释");
8 }
复制代码
三、@Transactional 注解应该只被应用到 public 可见度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 可是这个被注解的方法将不会展现已配置的事务设置。
四、@Transactional 注解能够被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,可以被能够识别 @Transactional 注解和上述的配置适当的具备事务行为的beans所使用。上面的例子中,其实正是 元素的出现 开启 了事务行为。
五、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你固然能够在接口上使用 @Transactional 注解,可是这将只能当你设置了基于接口的代理时它才生效。由于注解是不能继承的,这就意味着若是你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,并且对象也将不会被事务代理所包装(将被确认为严重的)。所以,请接受Spring团队的建议而且在具体的类上使用 @Transactional 注解。
本身写的一个测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class UserBaseInfoControllerTest {
@Autowired
private UserBaseInfoService userBaseInfoService;
@Test
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = RuntimeException.class)
public void testOne() {
UserBaseInfo userBaseInfo = new UserBaseInfo();
userBaseInfo.setId(3);
userBaseInfo.setUserName("xu2");
userBaseInfo.setPassWord("111111");
userBaseInfoService.update(userBaseInfo);
userBaseInfo.setUserName("xu1");
userBaseInfo.setPassWord("1111111");
userBaseInfoService.save(userBaseInfo);
}
}
@Service
public class UserBaseInfoServiceImpl extends AbstractService<UserBaseInfo> implements UserBaseInfoService {
@Resource
private UserBaseInfoMapper userBaseInfoMapper;
@Override
public void save(UserBaseInfo userBaseInfo) {
mapper.insertSelective(userBaseInfo);
}
@Override
//@Transactional(propagation = Propagation.REQUIRED,rollbackFor = RuntimeException.class)
//@Transactional(propagation=Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class)
//@Transactional(propagation=Propagation.MANDATORY,rollbackFor = RuntimeException.class)
//@Transactional(propagation=Propagation.NESTED,rollbackFor = RuntimeException.class)
//@Transactional(propagation=Propagation.NEVER,rollbackFor = RuntimeException.class)
//@Transactional(propagation=Propagation.NOT_SUPPORTED,rollbackFor = RuntimeException.class)
//@Transactional(propagation=Propagation.SUPPORTS,rollbackFor = RuntimeException.class)
public void update(UserBaseInfo userBaseInfo) {
mapper.updateByPrimaryKeySelective(userBaseInfo);
}
复制代码
假设有类A的方法methodB(),有类B的方法methodB().
若是B的方法methodB()的事务传播特性是propagation_required,那么以下图
A.methodA()调用B的methodB()方法,那么若是A的方法包含事务,则B的方法则不重新开启事务,
一、 若是B的methodB()抛出异常,A的methodB()没有捕获,则A和B的事务都会回滚;
二、 若是B的methodB()运行期间异常会致使B的methodB()的回滚,A若是捕获了异常,并正常提交事务,则会发生Transaction rolled back because it has been marked as rollback-only的异常。
三、 若是A的methodA()运行期间异常,则A和B的Method的事务都会被回滚
若是B的方法methodB()的事务传播特性是propagation_supports,么以下图
A.methodA()调用B的methodB()方法,那么若是A的方法包含事务,则B运行在此事务环境中,若是A的方法不包含事务,则B运行在非事务环境;
一、若是A没有事务,则A和B的运行出现异常都不会回滚。
二、若是A有事务,A的method方法执行抛出异常,B.methodB和A.methodA都会回滚。
三、若是A有事务,B.method抛出异常,B.methodB和A.methodA都会回滚,若是A捕获了B.method抛出的异常,则会出现异常Transactionrolled back because it has been marked as rollback-only。
表示当前方法必须在一个事务中运行,若是没有事务,将抛出异常,以下图调用关系:
B.methodB()事务传播特性定义为:PROPAGATION_MANDATORY
一、若是A的methoda()方法没有事务运行环境,则B的methodB()执行的时候会报以下异常:No existingtransaction found for transaction marked with propagation 'mandatory'
二、若是A的Methoda()方法有事务而且执行过程当中抛出异常,则A.methoda()和B.methodb()执行的操做被回滚;
三、若是A的methoda()方法有事务,则B.methodB()抛出异常时,A的methoda()和B.methodB()都会被回滚;若是A捕获了B.method抛出的异常,则会出现异常Transaction rolled back because ithas been marked as rollback-only
若有一下方法调用关系,如图:
B的methodB()定义的事务为PROPAGATION_NESTED;
一、 若是A的MethodA()不存在事务,则B的methodB()运行在一个新的事务中,B.method()抛出的异常,B.methodB()回滚,但A.methodA()不回滚;若是A.methoda()抛出异常,则A.methodA()和B.methodB()操做不回。
二、 若是A的methodA()存在事务,则A的methoda()抛出异常,则A的methoda()和B的Methodb()都会被回滚;
三、 若是A的MethodA()存在事务,则B的methodB()抛出异常,B.methodB()回滚,若是A不捕获异常,则A.methodA()和B.methodB()都会回滚,若是A捕获异常,则B.methodB()回滚,A不回滚;
5)PROPAGATION_NEVER
表示事务传播特性定义为PROPAGATION_NEVER的方法不该该运行在一个事务环境中
有以下调用关系:
若是B.methodB()的事务传播特性被定义为PROPAGATION_NEVER,则若是A.methodA()方法存在事务,则会出现异常Existingtransaction found for transaction marked with propagation 'never'。
6)PROPAGATION_REQUIRES_NEW
表示事务传播特性定义为PROPAGATION_REQUIRES_NEW的方法须要运行在一个新的事务中。
若有如下调用关系:B.methodB()事务传播特性为PROPAGATION_REQUIRES_NEW.
一、 若是A存在事务,A.methodA()抛出异常,A.methodA()的事务被回滚,但B.methodB()事务不受影响;若是B.methodB()抛出异常,A不捕获的话,A.methodA()和B.methodB()的事务都会被回滚。若是A捕获的话,A.methodA()的事务不受影响但B.methodB()的事务回滚。
表示该方法不该该在一个事务中运行。若是有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行。
若有一下调用关系图:
若是B.methodB()方法传播特性被定义为:PROPAGATION_NOT_SUPPORTED。
一、 若是A.methodA()存在事务,若是B.methodB()抛出异常,A.methodA()不捕获的话,A.methodA()的事务被回滚,而B.methodB()出现异常前数据库操做不受影响。若是A.methodA()捕获的话,则A.methodA()的事务不受影响,B.methodB()异常抛出前的数据操做不受影响。