美文网首页
@Transactional 解析与限制场景

@Transactional 解析与限制场景

作者: 万物皆有序和公式 | 来源:发表于2020-02-23 11:49 被阅读0次
image.png

列表1事务不会回滚
列表2事务不会回滚
@Transactional 注解解析
定义
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
...
}
@Transactional 的特性有:

可以在类或方法(接口)上标注

标注在类上:类中所有方法都进行事务处理
标注在接口、实现类的方法上:方法进行事务处理
优先级:方法注解 > 类注解

属性
value 和 transactionManager:事务管理器,PlatformTransactionManager 接口

propagation:事务传播机制,默认值为 Propagation.REQUIRED

事务传播机制 特性 备注
REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。 默认值,也是绝大多数场景
SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常。
NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。
isolation:事务隔离级别
事务隔离级别 特性 备注
DEFAULT 使用后端数据库默认的隔离级别 默认值,也是绝大多数场景
READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
timeout:事务超时时间,单位是秒。是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。默认值是当前数据库默认事务过期时间。

readOnly:事务是否是只读的,默认是 false。对于只读查询,可以指定事务类型为 readonly,即只读事务。由于只读事务不存在数据的修改, 因此数据库将会为只读事务提供一些优化手段

rollbackFor:设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则事务回滚。

rollbackForClassName:设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。

noRollbackFor:设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。

noRollbackForClassName:设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。

最后四个参数都与回滚有关。但是,不推荐使用 rollbackForClassName 和 noRollbackForClassName 两个参数,而用另外两个参数来代替,从参数的类型上就可以看出区别,使用字符串的缺点在于:如果不是用类的完整路径,就可能导致回滚设置对位于不同包中的同名类都生效;且如果类名写错,也无法得到 IDE 的动态提示。

使用 @Transactional 需要注意的地方
@Transactional 只能应用到 public 方法才有效
在默认配置中,Spring FrameWork 的事务框架代码只会将出现 runtime, unchecked 异常的事务标记为回滚;也就是说事务中抛出的异常是 RuntimeException 或其子类,这样事务才会回滚(默认情况下 Error 也会导致事务回滚)。但是,在默认配置的情况下,所有的 checked 异常都不会引起事务回滚。



Spring 中 @Transactional 注解的限制 - 没有事务的方法去调用有事务的方法
同一个类中,没有事务的方法去调用有事务的方法 - 事务会失效
示例代码
/**

  • <h2>在private方法上标注transactional, 事务无效</h2>

  • */
    @Transactional
    public void anotherOneSaveMethod() {

    extraAdDao.save(new ExtraAd("qinyi"));
    throw new RuntimeException();
    }

/**

  • <h2>同一个类中, 一个不标注事务的方法去调用 transactional 的方法, 事务会失效</h2>

  • */
    @Override
    // @Transactional
    public void NonTransactionalCanNotRollback() {

    anotherOneSaveMethod();
    }
    执行过程中抛出的异常
    java.lang.RuntimeException
    at com.imooc.extra.service.impl.SpringTransactionImpl.anotherOneSaveMethod(SpringTransactionImpl.java:130)
    at com.imooc.extra.service.impl.SpringTransactionImpl.NonTransactionalCanNotRollback(SpringTransactionImpl.java:140)
    at com.imooc.extra.service.impl.SpringTransactionImplFastClassBySpringCGLIBbafcf296.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:684)
    at com.imooc.extra.service.impl.SpringTransactionImplEnhancerBySpringCGLIBae164a74.NonTransactionalCanNotRollback(<generated>)
    at com.imooc.extra.service.TransactionTest.testNonTransactionalCanNotRollback(TransactionTest.java:55)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    ...
    从抛出的异常可以看到,首先,我们拿到的是代理对象,调用 NonTransactionalCanNotRollback 方法。但是 NonTransactionalCanNotRollback 方法在调用 anotherOneSaveMethod 的时候却是原始对象的 anotherOneSaveMethod。所以,这里的调用根本就没有事务的存在,导致事务失效。

不同类中,没有事务的方法去调用有事务的方法 - 事务不会失效
示例代码
@Service
public class AnotherSpringTransaction {

private final SpringTransactionImpl springTransaction;

@Autowired
public AnotherSpringTransaction(SpringTransactionImpl springTransaction) {
    this.springTransaction = springTransaction;
}

/**
 * <h2>不同类中, 一个不标注事务的方法去调用 transactional 的方法, 事务不会失效</h2>
 * */
public void TransactionalCanRollback() {

    springTransaction.anotherOneSaveMethod();
}

}
执行过程中抛出的异常
java.lang.RuntimeException
at com.imooc.extra.service.impl.SpringTransactionImpl.anotherOneSaveMethod(SpringTransactionImpl.java:130)
at com.imooc.extra.service.impl.SpringTransactionImplFastClassBySpringCGLIBbafcf296.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
从抛出的异常可以看到,首先,我们拿到的是代理对象,再去调用anotherOneSaveMethod。所以,这就有事务了,即事务不会失效。

相关文章

网友评论

      本文标题:@Transactional 解析与限制场景

      本文链接:https://www.haomeiwen.com/subject/pdzdqhtx.html