【问题标题】:@Transactional propogation_new being rolled back by parent Transaction@Transactional propogation_new 被父事务回滚
【发布时间】:2019-09-27 11:10:02
【问题描述】:

我有一个注解为@Transactional 的事件处理程序方法,该方法调用同一类中事件的实现。

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

如果由于检查而改变状态,我需要保持状态但事件失败以重试。

状态改变方法在另一个类中,方法用@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 的事务代码中调用了提交,但由于某种原因它没有持久化到数据库中。

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

我在这张照片中缺少什么?希望你能帮忙。

谢谢!

编辑

我想我找到了问题所在,稍微更改了示例代码以使其更清晰。

changeStatus 更改 crudService 返回的对象的状态。在实际应用中,我们会做更多的更改,因为依赖对象 o 的对象也需要根据状态更改进行更改。

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

【问题讨论】:

  • 该代码无法编译:changeStatus 是实例方法,而不是静态方法。 Y是春豆吗? t 对象如何获得 Y 的实例?
  • 是的,Y 是春豆。它通过 x 的构造函数注入。为澄清添加了构造函数。

标签: java spring-boot spring-transactions


【解决方案1】:

问题是因为第一个事务持有对状态改变的对象的引用。

当我们更改新事务中的状态时,我们提交该状态更改并返回。当我们返回时,外部事务恢复并抛出 RuntimeException,这会导致回滚。因为事务持有状态已更改的对象的状态,所以该对象会回滚到外部事务的状态,即旧状态。

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

然后我实现了一个检查异常,该异常在状态更改时被抛出,该异常被捕获然后抛出给父级。其他所有异常都会被捕获并发送一个 RuntimeException 来中断。

异常被父级捕获,服务将抛出 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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-27
    • 2018-05-26
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    相关资源
    最近更新 更多