likes
comments
collection
share

事务 还有这些用法,之前都不知道

作者站长头像
站长
· 阅读数 51

本文正在参加「金石计划 . 瓜分6万现金大奖」

transationTemplate.execute 的写法 第一次 碰到,我之前是 controller -> biz -> service -> mapper 然后用 @Transation 注解搞定事务,至于 同一个类的 方法之间调用,在biz 层就规避了

不懂就问

首先 同一个类中 方法调用因为没有 走 aop 所以 事务不生效,这个应该没毛病吧

问了人家,人家说 能解决 同一类中的 不同方法调用的 事务问题,我不太信。。验证下,如果能行,我以后加入

Spring事务失效的场景

  1. 非public修饰
  2. final修饰也会失效, 动态代理需要重写方法才能生效事务, final修饰则无法重写
  3. static修饰也会失效
  4. 吞了异常, catch住没有抛给spring
  5. 抛了错误的异常, catch住手动抛Exception则不会回滚, 因为spirng事务只会处理RuntimeException和Error
  6. 同一个类中的方法进行互相调用

准备代码

 public void a() {
//        transactionTemplate.execute(transactionStatus -> {
//            b();
//            c();
//            return true;
//        });
        Version version = new Version();
        version.setVersionName("test00");
        version.setProjectId(1L);
        versionService.save(version);
        b();
        c();
    }

    @Transactional(rollbackFor = Exception.class)
    public void b() {
        Version version = new Version();
        version.setVersionName("test01");
        version.setProjectId(1L);
        versionService.save(version);
    }

    @Transactional(rollbackFor = Exception.class)
    public void c() {
        Version version = new Version();
        version.setVersionName("test02");
        version.setProjectId(1L);
        versionService.save(version);
        int i = 1 / 0;
    }

第一次实验 嗯嗯 确实数据库插入了3条,事务没有生效嘛

那按照大哥的说法 如果用他那种写法 就能保证事务了?

改下代码 试验下

public void a() {
        transactionTemplate.execute(transactionStatus -> {
            Version version = new Version();
            version.setVersionName("test00");
            version.setProjectId(1L);
            versionService.save(version);
            b();
            c();
            return true;
        });
    }

想想 如果是和大哥的想法一样 数据库应该是 没有插入,好 执行下

嗯嗯 是的 确实没有数据库插入,但是为什么呢? 嗯嗯 大哥说得对

事务 还有这些用法,之前都不知道

猜想

我第一想法 就是 它这么写 就相当于 我们之前biz 层 对 service 层的调用,自然而言 就保证了事务,毕竟实现了 不同类的调用嘛

验证下

@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
    //判断transactionManager 是否为空 嗯嗯 没毛病
    Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
    //
    if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
        return ((CallbackPreferringPlatformTransactionManager)this.transactionManager).execute(this, action);
    } else {
        TransactionStatus status = this.transactionManager.getTransaction(this);

        Object result;
        try {
            //执行 真正的代码
            result = action.doInTransaction(status);
        } catch (Error | RuntimeException var5) {
            this.rollbackOnException(status, var5);
            throw var5;
        } catch (Throwable var6) {
            this.rollbackOnException(status, var6);
            throw new UndeclaredThrowableException(var6, "TransactionCallback threw undeclared checked exception");
        }

        this.transactionManager.commit(status);
        return result;
    }
}

扩展

transactionTemplate.executeWithoutResult

在看 transactionTemplate 方法的时候 发现一个 executeWithoutResult 方法,嗯嗯 他是干什么的

看个名字应该是 没有返回值 嗯嗯

default void executeWithoutResult(Consumer<TransactionStatus> action) throws TransactionException {
    this.execute((status) -> {
        action.accept(status);
        return null;
    });
}

这 。。。不就是套了一层,返回个null么

TransactionSynchronization

public class DoTransactionCompletion implements TransactionSynchronization {

    private Runnable runnable;

    public DoTransactionCompletion(Runnable runnable){
        this.runnable = runnable;
    }

    @Override public void afterCompletion(int status) {
        if(status == TransactionSynchronization.STATUS_COMMITTED){
            this.runnable.run();
        }
    }
}
public class TransactionUtils {
    public static void doAfterTransaction(DoTransactionCompletion doTransactionCompletion){
        if(TransactionSynchronizationManager.isActualTransactionActive()){
            TransactionSynchronizationManager.registerSynchronization(doTransactionCompletion);
        }
    }

    public static void main(String[] args) {
        TransactionUtils.doAfterTransaction(new DoTransactionCompletion(() -> {
            //事务成功才做的操作
        }));
    }
}

例子是 上面的main方法 实现的是 只有事务成功才会执行的操作

其余的可以自己进行判断status 进行控制

但是并不完美,这个写法 是有问题的

  1. 如果使用了 @Transaction REQUIRES_NEW的话 就会出现误完成
  2. 如果里面有多线程 ,也会导致 异常

优化

  1. 我后来把事务的new 单独进行判断,把REQUIRES_NEW的 忽略了
  2. 多线程 之前进行过封装,分为了 1 在同一个事务中,一起提交 2 可以局部提交,局部完成,我根据之前的封装,继续判断,如果是 1 状态,我会执行这个完成操作,否则进行剔除