Java 在 Spring bean 中启动新事务
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3037006/
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
Starting new transaction in Spring bean
提问by Marcus Leon
We have:
我们有:
@Transactional(propagation = Propagation.REQUIRED)
public class MyClass implementes MyInterface { ...
MyInterface has a single method: go()
.
MyInterface 有一个方法:go()
.
When go() executes we start a new transaction which commits/rollbacks when the method is complete - this is fine.
当 go() 执行时,我们开始一个新事务,该事务在方法完成时提交/回滚 - 这很好。
Now let's say in go() we call a private method in MyClass that has @Transactional(propagation = Propagation.REQUIRES_NEW
. It seems that Spring "ignores" the REQUIRES_NEW annotation and does not start a new transaction. I believe this is because Spring AOP operates on the interface level (MyInterface) and does not intercept any calls to MyClass methods. Is this correct?
现在让我们在 go() 中调用 MyClass 中的私有方法,该方法具有@Transactional(propagation = Propagation.REQUIRES_NEW
. Spring 似乎“忽略”了 REQUIRES_NEW 注释并且不会启动新事务。我相信这是因为 Spring AOP 在接口级别(MyInterface)上运行,并且不会拦截对 MyClass 方法的任何调用。这样对吗?
Is there any way to start a new transaction within the go() transaction? Is the only way to call anotherSpring managed bean that has transactions configured as REQUIRES_NEW?
有没有办法在 go() 事务中开始一个新的事务?是调用另一个将事务配置为 REQUIRES_NEW 的 Spring 托管 bean的唯一方法吗?
Update: Adding that when clients execute go()
they do so via a reference to the interface, not the class:
更新:补充说,当客户端执行时,go()
他们通过对接口的引用而不是类来执行:
@Autowired
MyInterface impl;
impl.go();
采纳答案by Abhinav Sarkar
From the Spring reference 2.5:
来自 Spring 参考 2.5:
When using proxies, the
@Transactional
annotation should only be applied to methods with public visibility. If you do annotate protected, private or package-visible methods with the@Transactional
annotation, no error will be raised, but the annotated method will not exhibit the configured transactional settings.
使用代理时,
@Transactional
注释应仅应用于具有公共可见性的方法。如果您使用注释对受保护的、私有的或包可见的方法进行@Transactional
注释,则不会引发错误,但带注释的方法不会显示配置的事务设置。
So Spring ignores @Transactional
annotation on non-public methods.
所以 Spring 忽略@Transactional
了非公共方法的注解。
Also,
还,
In proxy mode (which is the default), only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with
@Transactional
!
在代理模式下(默认),只有通过代理进入的“外部”方法调用才会被拦截。这意味着“自调用”,即目标对象中的方法调用目标对象的其他方法,即使被调用的方法被标记为
@Transactional
!也不会在运行时导致实际事务。
So even if you make your method public
, calling it from within a method of same class will not start a new transaction.
因此,即使您创建了方法public
,从同一类的方法中调用它也不会启动新事务。
You can use aspectj
mode in transaction settings so that the transaction related code is weaved in the class and no proxy is created at runtime.
您可以aspectj
在事务设置中使用模式,以便在类中编织与事务相关的代码,并且在运行时不创建代理。
See the reference documentfor more details.
有关更多详细信息,请参阅参考文档。
Another possible way of doing this is fetching the spring proxy of the class in the class itself and call methods on it rather than this
:
另一种可能的方法是在类本身中获取类的 spring 代理并调用它的方法,而不是this
:
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class SomeService {
@Autowired
private ApplicationContext applicationContext;
private SomeService getSpringProxy() {
return applicationContext.getBean(this.getClass());
}
private void doSomeAndThenMore() {
// instead of
// this.doSometingPublicly();
// do the following to run in transaction
getSpringProxy().doSometingPublicly();
}
public void doSometingPublicly() {
//do some transactional stuff here
}
}
回答by skaffman
@Transactional
will only be noticed if it's on a public
method, due to the way Spring AOP works.
@Transactional
public
由于 Spring AOP 的工作方式,只有在方法上才会被注意到。
However, you can programmatically start a new transactionif you desire, using TransactionTemplate
, e.g.
但是,您可以根据需要以编程方式启动新事务TransactionTemplate
,例如使用
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus status) {
// do stuff
}
});
回答by wmlynarski
In short you have to call the method though proxy to achieve the transactional behaviour. It is possible to call a "REQUIRES_NEW" in the same bean, as asked in the question. To do that you have to make a "self" reference. In spring its is not straightforward possible. You have to inject it with @Resource annotation.
简而言之,您必须通过代理调用该方法才能实现交易行为。正如问题中所问,可以在同一个 bean 中调用“REQUIRES_NEW”。为此,您必须进行“自我”引用。在春天,它不是直截了当的。你必须用@Resource 注释注入它。
@Service("someService")
public class ServieImpl implements Service {
@Resource(name = "someService")
Service selfReference;
@Transactional
public void firstMethod() {
selfReference.secondMethod();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void secondMethod() {
//do in new transaction
}
}
The invocation in firstMethod calls the proxy and not "this" which should make the "REQUIRES_NEW" transaction to work.
firstMethod 中的调用调用代理而不是“this”,这应该使“REQUIRES_NEW”事务起作用。