如何在Spring Transaction Management中调用自定义回滚方法?

Dav*_*rks 10 java spring transactions spring-mvc

环境:Spring 3,自定义事务管理,JDBC事务

我刚刚阅读了有关使用事务模板处理事务管理的Spring文档.它似乎过于复杂,所以我想问:

我的大部分交易都与JDBC有关,这意味着我只是声明@Transactional我的服务.但是现在我正在对另一个需要回滚的站点进行REST服务调用,如果以下任何JDBC操作失败,我将在这种情况下提供回滚代码.

正如我在我的方法进步,在我的交易-我想保存到REST服务调用的引用(回滚操作需要),以及异常时我只是希望有一个方法myCustomRollback()可以访问以前存储的对象.

为什么不在transactionTemplate中提供一个映射来存储东西并在@Transactional注释上定义自定义回滚方法?

这就是我想到的方式,我不会按照Spring的想法.有人可以帮助我弥合我想要的东西和我如何在Spring中最有效地完成它之间的差距吗?我只需要为一些特殊情况操作执行此操作.

Tim*_*imi 8

对于还在读这篇文章的人:

我解决了弹簧事件的类似问题 - 正如Den Roman在选项3中所建议的那样.这是基本思想(场景是虚构的):

每当我执行需要与事务一起回滚的外部操作时,我@Transactional使用spring(org.springframework.context.ApplicationEventPublisher)的支持在我的方法中发布一个事件:

@Transactional
public String placeOrder(Order order) {
    String orderId = orderServiceGateway.createOrder(order);
    applicationEventPublisher.publishEvent(new OrderCreatedEvent(orderId));
    workflowService.startWorkflow(orderId);
    return orderId;
}
Run Code Online (Sandbox Code Playgroud)

事件本身可以是任何对象 - 我创建了一个POJO,其中包含有关要删除的远程实体的详细信息.

然后我注册了一个绑定到事务阶段的特殊事件监听器 - 在我的例子中是回滚:

@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollBackOrder(OrderCreatedEvent orderCreatedEvent) {
    String orderId = orderCreatedEvent.getOrderId();
    orderServiceGateway.deleteOrder(orderId);
}
Run Code Online (Sandbox Code Playgroud)

当然,建议从回滚操作中捕获并记录异常,而不是从placeOrder()方法中丢失原始异常.

默认情况下,这些事件是同步的,但可以通过其他配置使它们异步.

这是一篇关于这种机制的非常好的文章,包括详细的配置和陷阱:事务同步和Spring应用程序事件(DZone)

虽然我不喜欢100%的解决方案,因为它使用事件发布的东西混淆了业务逻辑并且绑定到spring,它肯定会做我期望它做的事情,并且可以将事务方法中的上下文传递给回滚方法 - 这不是通过事务方法之外的传统try/catch块提供的(除非你把你的上下文放在异常本身,这不是很好).


Fic*_*ico 5

我已经重读了几次你的问题,我不确定我完全理解你的问题.我假设您正在执行someCode,如果失败,您想执行myCustomRollback,其中包含someCode的一些信息.所以我会尝试提供一个通用的答案.

如果你想让spring回滚一些代码.它只会回滚rollBackAble,就像jdbc事务一样.假设您有一个执行2次调用的方法.

@Transactional
public void doStuff(SomeEntity entity, File file) {
   persist(entity);
   customFileService.createOnFileSystem(file);
   throw new RunTimeException();
}
Run Code Online (Sandbox Code Playgroud)

所以上面的代码将始终回滚.它将撤消实体的持久性,但不会撤消文件的创建,因为它不是由Spring事务管理的,除非您为它提供自定义实现.

其次,Spring提供了两种处理事务的方法:

  • Spring AOP:在运行时创建一个代理,它将用事务性东西装饰你的代码.如果您的类名为MyClass,那么Spring将创建一个类名MyClassProxy,它将您的代码包装在事务代码中.
  • AspectJ:在编译时,您的.class文件将被调整,并且事务代码将嵌入您的方法中.

aspectJ方法似乎更难配置,但不是那么多,并且更容易使用.因为任何使用@Transactional注释的内容都将嵌入(编织)代码.对于Spring AOP,情况并非如此.例如在Spring中的事务内部方法调用将被忽略!因此aspectJ提供了一种更直观的方法.

回到我认为你的问题是(代码全部在1类):

public void doSomeCode() {
    Object restCall = initialize();
    try {
      execute(restCall);
    } catch (CustomException e) {
      myCustomRollback(restCall; e);
    }
}

@Transactional(rollbackFor = CustomException.class)
private void execute(Object restCall) throws CustomException {
    // jdbc calls..
      restCall = callRest(restCall);
      throw new CustomException();
}

void myCustomRollback(Object restCall, CustomException e) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

上面的代码只适用于AspectJ!由于你的内部方法调用似乎也是私有的!运行时的AOP无法处理此问题.

那么所发生的事情是执行中的所有内容(即rollbackAble)都将被回滚.在doStuff中,您可以获得有关执行中使用的对象的信息,您现在可以在myCustomRollback中手动回滚REST内容.

不确定我是否正确回答了这个问题,但我希望它可以帮助有类似问题的人.


Ana*_*rma -1

您可以使用 AfterThrowing 建议(当抛出异常时)并myCustmRollback()在那里调用您的方法(),您可以使用TransactionSynchronizationManager类来获取当前事务并将其回滚...

或者..您可以使用aroundadvice来开始&提交/回滚您的事务(这样您可以通过使用类来使用spring提供的事务管理器TransactionSynchronizationManager