当前位置: 首页 > 知识库问答 >
问题:

@Transactional propogation_new正由父事务回滚

史涵育
2023-03-14

我有一个eventhandler方法,它被注释为@Transactional,这个方法在同一个类中调用一个事件的实现。

此事件执行一些检查,根据结果,它将执行某些操作,或者更改状态并抛出RuntimeException。

status change方法在另一个类中,并且该方法用@transactional(propagation=propagation.requires_new)注释。

我预计,由于内部事务完成,状态更改将被持久化,事件事务将回滚。

我看到的是状态更改也回滚了,但我不明白为什么当我明确地告诉它为状态更改创建一个新事务时,它会回滚所有内容。

public class t implements it {
    // Do initialisation and class injection. Y is constructor injected
    private final Y y;

    public t(Y y) {
       this.y = y;
    }

    @Override
    @Transactional
    public void handleEvent(EventContext context) {
        switch (context.getEventType()) {
            case event:
                validate(context);
                break;
        }
    }

    private void validate(EventContext context) {
        Object o = crudService.findByProperty(context.getObjectUuid());
        if (!o.check) {
            y.changeStatus(ERROR);
            // break for retry
            throw new RuntimeException("Some serious message log");
        } else {
            // do some stuff
        }
    }
}

public class Y implements IY {
   
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void changeStatus(Object o, String status) {
        // We do a lot more here then just change this status because of inheriting objects but for the sake of the argument, change status
        o.status = status;
    }
}




这是代码正在做什么的一个粗略的草稿。

我希望状态更改能够持久化,因为当propogation_new事务启动时外部事务会暂停。我还可以看到在Spring的事务代码中调用了commit,但由于某种原因,它没有持久化到数据库中。

如果我移除运行时异常的抛出,它可以工作,但事件会完成,这是不需要的。

changeStatus更改由CrudService返回的对象的状态。在实际的应用程序中,我们要做更多的更改,因为依赖于object o的对象也需要在状态更改时进行更改。

因为外部事务具有o的状态,那是否意味着如果我在内部事务内部进行更改,因为外部事务持有引用,它将回滚到那个状态,而不是持久化内部事务的更改?

共有1个答案

祝宏放
2023-03-14

造成问题的原因是第一个事务持有对其状态已更改的对象的引用。

当我们在新事务中更改状态时,我们提交状态更改并返回。当我们返回时,外部事务恢复并抛出RuntimeException,这导致回滚。由于事务保存状态已更改的对象的状态,该对象将回滚到外部事务具有的状态,即旧状态。

为了解决这个问题,我不是只在新事务中进行状态更改,而是将所有逻辑移到它自己的事务中,并删除状态更改时的事务。

然后,我实现了一个检查异常,它在状态更改时被抛出,然后被捕获并抛出给父级。每一个其他异常都会被捕获并发送一个RuntimeException到Break。

异常被父级捕获,服务将抛出RuntimeException。由于内部事务已完成并提交(在检查异常的情况下),状态将保持更改,事件将失败重试。

在我的场景中,我将逻辑移到了它自己的类/方法中,您也可以将代码留在同一个类中,但您必须实现它本身的代理,并使用该代理通过您的方法调用Spring代理,否则它将忽略该方法上的事务性语句。

下面是它看起来的最终草稿。

public class t implements it {
    // Do initialisation and class injection. Y is constructor injected
    private final B b;

    public t(B b) {
       this.b = b;
    }

    @Override
    @Transactional
    public void handleEvent(EventContext context) {
        switch (context.getEventType()) {
            case event:
                validate(context);
                break;
        }
    }

    // You can skip this method and simply call b, but in my scenario we do a couple of other things that do not have to be part of the transaction
    private void validate(EventContext context) {
        try {
            b.allLogicMethod(context.getObjectUuid());
        } catch(Exception e) {
            // Here we break the event so we can retry it, but the transaction succeeded in case it was a checked Exception
            throw new RuntimeException(e);
        }
    }
}

public class b implements IB {

    private final Y y;

    Public B(Y y) {
        this.Y = y;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void allLogicMethod(String uuid) {
        try {
            Object o = crudService.findByProperty(context.getObjectUuid());
            if (!o.check) {
                y.changeStatus(o, ERROR);
                // break for retry
                throw new CheckedException("Some serious message log");
            } else {
                // do everything else
            }
        } catch(CheckedException ce) {
            throw ce;
        } catch(Exception e) {
            throw new RuntimeException("some message", e);
        }
    }
}

public class Y implements IY {

    @Override
    public void changeStatus(Object o, String status) {
        // We do a lot more here then just change this status because of inheriting objects but for the sake of the argument, change status
        o.status = status;
    }
}
 类似资料:
  • 我正在与用propagation.requires_new注释的方法的奇怪行为作斗争。 以下是TransactionManager的日志:

  • 我的路线如下 我知道(A)处的JMS消费者将在每次轮询时分叉JMS事务,并附加到线程。(B)中的事务处理节点也将在交换到达那里并连接到线程后分叉JPA事务。 请在下面找到我的问题: > < li >能否将两个不同的事务附加到一个线程上(如上所示)? < li >如果是,哪一个应该被停职? < li> 上述路由的提交和回滚顺序应该是什么? 注:我没有从骆驼在行动第二版中找到任何明显的答案,所以请指导

  • 问题内容: 我在EJB3无状态会话Bean中使用CMT。另外,我还创建了自己的具有注释“ @ApplicationException(rollback = true)”的异常。 要回滚事务时是否必须使用“ context.setRollbackOnly()”? 我可以通过在bean的public方法内抛出异常来回滚事务吗? 如果是这样(对Q#2的回答是“是”),我是否必须通过在方法中声明异常来将异

  • 我正在使用spring-test运行JUnit测试,我的代码如下所示 我的问题是我希望我的测试不影响其他测试。所以我想为每个测试创建一些类似回滚的东西。我为此找了很多,但到目前为止一无所获。我使用Hibernate和MySql来实现这个

  • -ZJ 以下是我在Application.Properties中的数据源设置:

  • 我在Spring Boot应用程序中有一个Javers实现。Mongo4.4被用作数据库。从MongoDB4.4开始,您可以在事务中创建文档。 我在创建对象时模拟了一个异常。如预期的那样,对象没有在数据库中创建,但是一个新的快照被添加到jv_snapshots集合中。 控制器: