java 如何在 Spring 事务管理中调用自定义回滚方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5684473/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to call a custom rollback method in Spring Transaction Management?
提问by David Parks
Environment: Spring 3, Custom Transaction Management, JDBC Transactions
环境:Spring 3、自定义事务管理、JDBC 事务
I just read the Spring docs on using the transaction template to handle transaction management. It seemed overly complexso I want to ask:
我刚刚阅读了有关使用事务模板处理事务管理的 Spring 文档。看起来过于复杂所以我想问:
Most of my transactions are JDBC related, meaning I just declare an @Transactional
on my service. But now I am making a REST service call to another site which needs to rollback if any of the following JDBC operations fail, I'll provide the rollback code in this case.
我的大部分事务都与 JDBC 相关,这意味着我只是@Transactional
在我的服务上声明了一个。但是现在我正在对另一个站点进行 REST 服务调用,如果以下任何 JDBC 操作失败,该站点需要回滚,我将在这种情况下提供回滚代码。
As I progress in my method, in my transaction - I want to save a reference to the REST service call(needed to roll back that action), and upon exception I just want a method myCustomRollback()
calledwhich can access the previously stored object.
随着我的方法的进展,在我的事务中 - 我想保存对 REST 服务调用的引用(需要回滚该操作),并且出现异常时我只想要myCustomRollback()
调用一个可以访问先前存储的对象的方法。
Why not just provide a map in the transactionTemplate for storing stuff and define a custom rollback method on the @Transactional
annotation?
为什么不在 transactionTemplate 中提供一个映射来存储东西并在@Transactional
注释上定义一个自定义回滚方法?
This is the way I think about it, I'm not following the way Spring thinks about this. Can someone help me bridge the gap between what I want and how I accomplish it most efficiently in Spring? I only need to do this for a few special case operations.
这是我的想法,我没有遵循 Spring 的想法。有人可以帮助我弥合我想要的东西和我如何在 Spring 中最有效地完成它之间的差距吗?我只需要为一些特殊情况操作执行此操作。
采纳答案by Anantha Sharma
you can use the AfterThrowing advice (when an exception is thrown) & call your method (myCustmRollback()
) there, you can use TransactionSynchronizationManager
class to get thecurrent transaction & roll it back...
您可以使用 AfterThrowing 建议(当抛出异常时)并在myCustmRollback()
那里调用您的方法(),您可以使用TransactionSynchronizationManager
类来获取当前事务并将其回滚...
alternatively.. you can use the AroundAdvice to begin & commit/rollback your transaction (this way you can use the spring provided transaction manager by using the TransactionSynchronizationManager
class)
或者..你可以使用AroundAdvice开始&提交/回滚你的事务(这样你可以通过使用TransactionSynchronizationManager
类来使用spring提供的事务管理器)
回答by Timi
To anyone still reading this:
对于仍在阅读本文的任何人:
I solved a similar problem with spring events - as suggested by Den Romanin option 3. Here's the basic idea (scenario is fictional):
我解决了春季事件的类似问题 - 正如Den Roman在选项 3 中所建议的那样。这是基本思想(场景是虚构的):
Whenever I perform external operations that need to be rolled back together with the transaction, I publish an event inside my @Transactional
method using support from spring (org.springframework.context.ApplicationEventPublisher
):
每当我执行需要与事务一起回滚的外部操作时,我都会@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;
}
The event itself can be any object - I created a POJO with details about the remote entity to be deleted.
事件本身可以是任何对象 - 我创建了一个 POJO,其中包含有关要删除的远程实体的详细信息。
Then I registered a special event listener that is bound to a transaction phase - in my case to the rollback:
然后我注册了一个绑定到事务阶段的特殊事件侦听器 - 在我的例子中是回滚:
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollBackOrder(OrderCreatedEvent orderCreatedEvent) {
String orderId = orderCreatedEvent.getOrderId();
orderServiceGateway.deleteOrder(orderId);
}
Of course, it's recommended to catch & log the exception from rollback operation, not to lose the original exception from the placeOrder()
method.
当然,建议在回滚操作中捕获并记录异常,不要丢失方法中的原始异常placeOrder()
。
By default these events are synchronous, but they can be made async by additional configuration.
默认情况下,这些事件是同步的,但可以通过其他配置使它们异步。
Here's a very good article on this mechanism, including detailed configuration and pitfalls: Transaction Synchronization and Spring Application Events (DZone)
这里有一篇关于这种机制的非常好的文章,包括详细的配置和陷阱:事务同步和Spring应用程序事件(DZone)
While I don't like the solution 100% because it clutters the business logic with event publishing stuff and binds to spring, it definitely does what I expect it to do and makes it possible to pass context from the transactional method to the rollback method - which is not available through a traditional try/catch block outside of the transactional method (unless you put your context in the exception itself, which is not very nice).
虽然我 100% 不喜欢该解决方案,因为它用事件发布内容混淆了业务逻辑并绑定到 spring,但它确实可以满足我的期望,并且可以将上下文从事务方法传递到回滚方法 -这不能通过事务方法之外的传统 try/catch 块使用(除非您将上下文放在异常本身中,这不是很好)。
回答by Fico
I've re-read your question a few times and am not sure I understand your question completely. I assume your executing someCodeand if that fails you would like to execute myCustomRollbackwhich has some information about someCode. So I'll try to provide a Generic answer.
我已经重新阅读了您的问题几次,但不确定我是否完全理解您的问题。我假设您执行someCode,如果失败,您希望执行myCustomRollback,其中包含有关 someCode 的一些信息。所以我会尝试提供一个通用的答案。
If you want spring to rollback some code. It will only rollback that which is rollBackAble, like jdbc transactions. Assume you have a method which performs 2 calls.
如果你想让 spring 回滚一些代码。它只会回滚那些是 rollBackAble 的东西,比如 jdbc 事务。假设您有一个执行 2 次调用的方法。
@Transactional
public void doStuff(SomeEntity entity, File file) {
persist(entity);
customFileService.createOnFileSystem(file);
throw new RunTimeException();
}
So the code above will always rollback. It will undo the persisting of your entity, but not the creation of your file, since that is not managed by Spring transactions, unless you provide custom implementation for it to be.
所以上面的代码总是会回滚。它会撤消实体的持久化,但不会撤消文件的创建,因为这不是由 Spring 事务管理的,除非您为其提供自定义实现。
Second, Spring provides 2 ways of working with transactions:
其次,Spring 提供了两种处理事务的方式:
- Spring AOP: a proxy is created at runtimewhich will decorate your code with transactional stuff. If your class would be named MyClass, then Spring will create a class names MyClassProxy, which will wrap your code in transactional code.
- AspectJ: at compile time your .class file will be adjusted and transactional code will be embedded inside your method.
- Spring AOP:在运行时创建一个代理,它将用事务性的东西装饰你的代码。如果您的类将命名为 MyClass,那么 Spring 将创建一个名为 MyClassProxy 的类,它将您的代码包装在事务代码中。
- AspectJ:在编译时,您的 .class 文件将被调整,事务代码将嵌入您的方法中。
The aspectJ approach seems harder to configure, but isn't so much and is way easier to use. Since anything which is annotated with @Transactional will be embedded (weaved) with code. For Spring AOP this is not the case. Transactional inner method calls for instance in Spring will be ignored! So aspectJ provides a more intuitive approach.
aspectJ 方法似乎更难配置,但没有那么多,而且更容易使用。因为任何用@Transactional 注释的东西都将嵌入(编织)代码。对于 Spring AOP,情况并非如此。例如,Spring 中的事务性内部方法调用将被忽略!所以aspectJ 提供了一种更直观的方法。
Back to what I think your question is (the code is all in 1 class):
回到我认为你的问题是(代码都在 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) {
...
}
The code above will only work with AspectJ!Since your making inner method calls which also seems to be private! AOP at runtime cannot handle this.
上面的代码只适用于 AspectJ!由于您进行内部方法调用,这似乎也是私有的!运行时的 AOP 无法处理这个问题。
So what happens is everything (which is rollbackAble) in execute will be rollbacked. And in doStuff you have information about the objects which were used in execute, you now can use in myCustomRollback to rollback your REST stuff manually.
因此,执行中的所有内容(即 rollbackAble)都将被回滚。在 doStuff 中,您有关于执行中使用的对象的信息,现在您可以在 myCustomRollback 中使用手动回滚 REST 内容。
Not sure if I answered this question properly, but I hope it helps someone with a similar problem.
不确定我是否正确回答了这个问题,但我希望它可以帮助有类似问题的人。
回答by Den Roman
1 solution is to implement your own transactional manager by extending a one
1 种解决方案是通过扩展一个来实现您自己的事务管理器
2 solution is to use TransactionSynchronizationManager class
2 解决方案是使用 TransactionSynchronizationManager 类
3 solution is to use @TransactionalEventListener in case you have Spring 4
3 解决方案是使用 @TransactionalEventListener 以防您有 Spring 4
回答by zudokod
Spring transaction management the default behavior for automatic rollback is for unchecked exceptions
Spring 事务管理自动回滚的默认行为是针对未经检查的异常
so for a custom exception,
所以对于自定义异常,
@Transactional(rollbackFor = CustomException.class, noRollbackFor = RuntimeException.class)
public void doSomething(...
)
the transaction be rolled back if it there is an exception that matches the specified. If an exception not matches, it is propagated to caller of the service or TransactionRolledBackException wrapper
如果存在与指定匹配的异常,则事务将被回滚。如果异常不匹配,则将其传播给服务的调用者或 TransactionRolledBackException 包装器
if you use use the org.springframework.transaction.PlatformTransactionManager it is more manageable handling exceptions than template
如果您使用 org.springframework.transaction.PlatformTransactionManager 它比模板更易于管理处理异常
check the documentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
检查文档http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html