Spring事务?!进来秒懂

Java传家宝:微信公众号(Java传家宝)、Java传家宝-B站Java传家宝-知乎Java传家宝-CSND

Spring 事务

​ 首先,确认一点,Spring中的事务都是依赖于数据库实现的。Spring 并不直接管理事务,而是提供了多种事务管理器PlatformTransactionManager就是Spring事务的管理接口,另外TransactionDefinition 定义了事务的隔离级别、传播行为、超时、回滚规则等等,TransactionStatus定义事务运行状态。先看一下实现:

  • PlatformTransactionManager
public interface PlatformTransactionManager extends TransactionManager {
    // 获取事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
	// 提交事务
    void commit(TransactionStatus var1) throws TransactionException;
	// 回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}
  • TransactionDefinition
public interface TransactionDefinition {
    // 传播级别
    int PROPAGATION_REQUIRED = 0; // 默认
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
    // 隔离级别
    int ISOLATION_DEFAULT = -1; // 默认
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    // 超时时间
    int TIMEOUT_DEFAULT = -1;
//....
}
  • TransactionStatus
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    // TransactionExecution
    boolean isNewTransaction(); // 是否是新事物
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted();
    //....
    boolean hasSavepoint(); // 是否有恢复点
}

编程式事务

​ 这个大概了解一下,实际不怎么使用。可以通过transactionTemplate或者transactionManager手动管理事务。大概就是新建事务,然后通过try…catch实现提交和回滚逻辑。示例如下:

  • transactionTemplate示例
@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {	
            try {
                // ....  业务代码
            } catch (Exception e){
                //回滚
                transactionStatus.setRollbackOnly();
            }
        }
    });
}
  • transactionManager示例
@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {

  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}

声明式事务

​ 实际中,我们常用的是这个,通过注解@Transactional,先看一下实现:

public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";
    @AliasFor("value")
    String transactionManager() default "";
    String[] label() default {};
    Propagation propagation() default Propagation.REQUIRED; // 传播行为 默认REQUIRED
    Isolation isolation() default Isolation.DEFAULT; // 隔离级别 默认DEFAULT
    int timeout() default -1; // 超时时间默认-1,即没有
    String timeoutString() default "";
    boolean readOnly() default false;
    Class<? extends Throwable>[] rollbackFor() default {};
    String[] rollbackForClassName() default {};
    Class<? extends Throwable>[] noRollbackFor() default {};
    String[] noRollbackForClassName() default {};
}
Propagation

​ 事务传播级别,可分为以下几种,默认为Propagation.REQUIRED:

  • Propagation.REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。
  • Propagation.REQUIRES_NEW:当前存在事务,挂起。新建一个事务。
  • Propagation.NESTED:当前存在事务,则嵌套一个新得小事务,小事务回滚不影响外部事务,而外部事务回滚会导致小事务回滚。
  • Propagation.MANDATORY:支持当前事务,当前不存在事务抛出异常
  • Propagation.SUPPORTS:支持当前事务,当前不存在事务不做任何操作
  • Propagation.NOT_SUPPORTED:当前存在事务,挂起。反之不做任何操作
  • Propagation.NEVER:当前存在事务,抛出异常。反之不做任何操作

支持当前事务:是指当前存在事务得话,就加入该事务,随着事务提交或者回滚

反之不做任何操作:是指不创建新得事务,以非事务状态运行

Isolation

​ 事务隔离级别,可分为以下几种,默认为DEFAULT:

  • Isolation.DEFAULT:采用数据库默认得隔离级别,比如Innodb引擎得Mysql就使用的可重复读隔离级别Isolation.REPEATABLE_READ
  • Isolation.READ_UNCOMMITTED:读未提交,表示当前事务可以读取其他事务还没有提交的数据,无法避免并发事务问题
  • Isolation.READ_COMMITTED:读已提交,表示当前事务可以读取已经提交过的数据,可以避免脏读问题
  • Isolation.REPEATABLE_READ:可重复读,表示当前事务重复读取数据,读取到的数据不变,可以避免脏读不可重复读问题
  • Isolation.SERIALIZABLE:可串行化,表示当前事务按照串行执行,即同一时间只执行一条事务,可避免所以并发事务问题

并发事务问题:为便于理解,假定事务A和事务B正在并发执行,然后假定数据id=2

  • 脏读:A读取了B修改了但还未提交的id=3,但是此时B回滚了,id仍然=2,A读取到的id=3就相当于是错误的,造成脏读
  • 不可重复读:A读取了id=2,此时B修改了id=3并提交,A此刻再次读取id=3,两次读取不一致,造成不可重复读
  • 幻读:A读取某个字段不存在,此时B插入了该字段并提交,A再次读取该字段,发现存在了,那么此时就造成幻读
其他属性
  • timeout:表示超时时间,默认为-1即没有超时时间或者取决后端数据库的设置。如果设置了那么当事务超过该时间还没有完成,那么自动回滚。
  • readOnly:表示只读,默认为false。如果设置为true,那么该事务只读不可写,可用于只做查询的事务。
  • rollbackFor:回滚策略,默认只回滚RuntimeException和Error。也可以设置回滚自定义异常,传入class对象数组即可,还有对应的可以设置类名数组rollbackForClassName属性
  • noRollbackFor:不回滚的异常,同理还有noRollbackForClassName属性

事务注解使用

​ @Transactional注解可作用于public修饰的类、方法、接口中,具体效果如下:

  • 作用于类:当前类的所以public方法都会赋予相同的事务
  • 作用于方法:当类配置了注解,方法也配置了注解,方法的事务会覆盖类的事务配置信息
  • 作用于接口:不推荐,在接口使用事务注解并且配置了 AOP 使用 CGLib 动态代理将会导致其失效

事务失效

​ 实际应用中可能会出现添加了@Transactional注解,但数据没有回滚的问题,即事务失效,看下面的总结。

非public方法

​ 由于Spring事务是通过Spring AOP产生代理对象实现的,在 Spring AOP 代理时,TransactionInterceptor(事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 Intercept 方法或 JDKDynamicAopProxyinvoke 方法会间接调用 AbstractFallbackTransationAttributeSourcecomputeTransactionAttribute 方法,获取 @Transactional 注解的事务配置信息。

1 protected TransactionAttribute computeTransactionAttribute(Method method,
2    Class<?> targetClass) {
3        // Don't allow no-public methods as required. 
4        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
5        return null;
6    }

此方法会检查目标方法的修饰符是否为 public,非 public 作用域则不会获取 @transactional 的属性配置信息。其中 protected、private 修饰的方法上使用 @Transactional 注解,事务会失效但不会有任何报错。

属性配置错误
  • Propagation配置为SUPPORTS、NOT_SUPPORTS和NEVER时,自身不会产生事务,SUPPORTS依赖当前事务,NOT_SUPPORTS会挂起当前事务,NEVER存在当前事务抛出异常。
  • rollbackFor回滚策略可以配置不回滚的异常,配置错误导致应该回滚的异常没有回滚,事务失效
同类方法调用,事务不生效

​ 在同一个类中,该类A方法调用了该类的某个事务方法B,那么在调用A方法时,就会间接导致B方法的事务失效

解决办法:

1、通过IOC容器拿到对应的代理对象即可。可以使用AopContext().currentProxy拿到;或者直接将本类对象直接注入即可。

2、将事务方法拿到该类之外

多线程任务

​ 在事务方法内,如果采用了多线程,多线程内的方法不被事务控制。

事务方法内异常被捕获

​ 在事务方法内,如果出现需要回滚的异常在方法内被try/catch捕获处理了,那么事务不会回滚。

事务方法未被 Spring 管理

​ Spring的事务是通过AOP实现的,而Spring AOP是基于Spring的IOC容器的,只会对IOC内部的Bean生效。因此如果事务方法对应的类不在IOC容器中,即不被Spring管理,那么事务失效。

未配置事务管理器

​ 开启事务不仅需要添加@Transactional注解,还需要为IOC容器添加事务管理器,如下:

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}

没有添加,则事务失效。或者在主类添加**@EnableTransactionManagement**实现一样的效果。

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值