@Transactional propogation_new被父事务回滚

时间:2019-05-09 15:04:14

标签: java spring-boot spring-transactions

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

此事件进行一些检查,并根据结果将执行某些操作,或者更改状态并引发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,这是否意味着如果我在内部事务内进行更改,因为外部事务拥有引用,它将回滚到该状态,而不是保留内部事务的更改?

1 个答案:

答案 0 :(得分:0)

由于第一个事务持有对状态更改的对象的引用而导致问题。

当我们在新交易中更改状态时,我们提交该状态更改并返回。当我们返回时,外部事务将继续并引发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;
    }
}