@Transactional propogation_new 被父事务回滚
@Transactional propogation_new being rolled back by parent Transaction
我有一个带有@Transactional 注释的事件处理程序方法,该方法在同一 class.
中调用一个事件的实现
此事件会进行一些检查,并根据结果执行某些操作,或者更改状态并抛出 RuntimeException。
如果状态因为检查而改变,我需要状态保持不变但事件失败以重试。
改变状态的方法在另一个class中,方法注解为@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,这是否意味着如果我在内部事务中进行更改,因为外部事务持有引用,它将回滚到该状态而不是持久化内部交易?
问题是因为第一个事务持有对状态改变的对象的引用。
当我们更改新事务中的状态时,我们提交该状态更改并 return。当我们 return 时,外层事务恢复并抛出 RuntimeException,从而导致回滚。因为事务保存状态已更改的对象的状态,所以该对象将回滚到外部事务所具有的状态,即旧状态。
为了解决这个问题,我不再只在新事务中更改状态,而是将所有逻辑都移到了它自己的事务中,并在状态更改时删除了事务。
然后我实现了一个已检查的异常,该异常在状态更改时被抛出,该异常被捕获然后抛给父级。每个其他异常都会被捕获并发送一个 RuntimeException 来中断。
父级捕获异常,服务将抛出 RuntimeException。因为内部事务完成并提交(在检查异常的情况下)状态保持更改并且事件重试失败。
在我的场景中,我将逻辑移到了它自己的 class/method,您也可以将代码保留在相同的 class 但您必须实现自己的代理并使用该代理来通过您的方法调用 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;
}
}
我有一个带有@Transactional 注释的事件处理程序方法,该方法在同一 class.
中调用一个事件的实现此事件会进行一些检查,并根据结果执行某些操作,或者更改状态并抛出 RuntimeException。
如果状态因为检查而改变,我需要状态保持不变但事件失败以重试。
改变状态的方法在另一个class中,方法注解为@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,这是否意味着如果我在内部事务中进行更改,因为外部事务持有引用,它将回滚到该状态而不是持久化内部交易?
问题是因为第一个事务持有对状态改变的对象的引用。
当我们更改新事务中的状态时,我们提交该状态更改并 return。当我们 return 时,外层事务恢复并抛出 RuntimeException,从而导致回滚。因为事务保存状态已更改的对象的状态,所以该对象将回滚到外部事务所具有的状态,即旧状态。
为了解决这个问题,我不再只在新事务中更改状态,而是将所有逻辑都移到了它自己的事务中,并在状态更改时删除了事务。
然后我实现了一个已检查的异常,该异常在状态更改时被抛出,该异常被捕获然后抛给父级。每个其他异常都会被捕获并发送一个 RuntimeException 来中断。
父级捕获异常,服务将抛出 RuntimeException。因为内部事务完成并提交(在检查异常的情况下)状态保持更改并且事件重试失败。
在我的场景中,我将逻辑移到了它自己的 class/method,您也可以将代码保留在相同的 class 但您必须实现自己的代理并使用该代理来通过您的方法调用 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;
}
}