Java Spring @Transaction 方法被同一个类中的方法调用,不起作用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3423972/
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
Spring @Transaction method call by the method within the same class, does not work?
提问by Mike
I am new to Spring Transaction. Something that I found really odd, probably I did understand this properly.
我是 Spring Transaction 的新手。我觉得很奇怪的事情,可能我确实理解正确。
I wanted to have a transactional around method level and I have a caller method within the same class and it seems like it does not like that, it has to be called from the separate class. I don't understand how is that possible.
我想在方法级别上进行事务处理,并且我在同一个类中有一个调用方方法,但它似乎不喜欢那样,必须从单独的类中调用它。我不明白这怎么可能。
If anyone has an idea how to resolve this issue, I would greatly appreciate. I would like to use the same class to call the annotated transactional method.
如果有人知道如何解决这个问题,我将不胜感激。我想使用同一个类来调用带注释的事务方法。
Here is the code:
这是代码:
public class UserService {
@Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
回答by Espen
It's a limitation of Spring AOP(dynamic objects and cglib).
这是Spring AOP(动态对象和cglib)的一个限制。
If you configure Spring to use AspectJto handle the transactions, your code will work.
如果您将 Spring 配置为使用AspectJ来处理事务,则您的代码将起作用。
The simple and probably best alternative is to refactor your code. For example one class that handles users and one that process each user. Then defaulttransaction handling with Spring AOP will work.
简单且可能最好的替代方法是重构您的代码。例如,一个处理用户的类和一个处理每个用户的类。然后使用 Spring AOP 的默认事务处理将起作用。
Configuration tips for handling transactions with AspectJ
使用 AspectJ 处理事务的配置技巧
To enable Spring to use AspectJ for transactions, you must set the mode to AspectJ:
要使 Spring 能够使用 AspectJ 进行事务,您必须将模式设置为 AspectJ:
<tx:annotation-driven mode="aspectj"/>
If you're using Spring with an older version than 3.0, you must also add this to your Spring configuration:
如果您使用的 Spring 版本低于 3.0,则还必须将其添加到 Spring 配置中:
<bean class="org.springframework.transaction.aspectj
.AnnotationTransactionAspect" factory-method="aspectOf">
<property name="transactionManager" ref="transactionManager" />
</bean>
回答by Kai
The problem here is, that Spring's AOP proxies don't extend but rather wrap your service instance to intercept calls. This has the effect, that any call to "this" from within your service instance is directly invoked on that instance and cannot be intercepted by the wrapping proxy (the proxy is not even aware of any such call). One solutions is already mentioned. Another nifty one would be to simply have Spring inject an instance of the service into the service itself, and call your method on the injected instance, which will be the proxy that handles your transactions. But be aware, that this may have bad side effects too, if your service bean is not a singleton:
这里的问题是,Spring 的 AOP 代理不会扩展而是包装您的服务实例以拦截调用。这样做的效果是,从您的服务实例中对“this”的任何调用都直接在该实例上调用,并且不能被包装代理拦截(代理甚至不知道任何此类调用)。已经提到了一种解决方案。另一个漂亮的方法是简单地让 Spring 将服务的一个实例注入到服务本身中,并在注入的实例上调用您的方法,这将是处理您的事务的代理。但请注意,如果您的服务 bean 不是单例,这也可能有不好的副作用:
<bean id="userService" class="your.package.UserService">
<property name="self" ref="userService" />
...
</bean>
public class UserService {
private UserService self;
public void setSelf(UserService self) {
this.self = self;
}
@Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
self.addUser(user.getUserName, user.getPassword);
}
}
}
回答by LionH
You can autowired BeanFactory inside the same class and do a
您可以在同一个类中自动装配 BeanFactory 并执行
getBean(YourClazz.class)
getBean(YourClazz.class)
It will automatically proxify your class and take into account your @Transactional or other aop annotation.
它会自动代理您的类并考虑您的 @Transactional 或其他 aop 注释。
回答by Hlex
This is my solution for self invocation:
这是我的自我调用解决方案:
public class SBMWSBL {
private SBMWSBL self;
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void postContruct(){
self = applicationContext.getBean(SBMWSBL.class);
}
// ...
}
回答by Almas Abdrazak
With Spring 4 it's possible to Self autowired
使用 Spring 4 可以自动装配
@Service
@Transactional
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository repository;
@Autowired
private UserService userService;
@Override
public void update(int id){
repository.findOne(id).setName("ddd");
}
@Override
public void save(Users user) {
repository.save(user);
userService.update(1);
}
}
回答by Ujjwal Choudhari
The issue is related to how spring load classes and proxies. It will not work , untill you write your inner method / transaction in another class or go to other class and then again come to your class and then write the inner nested transcation method.
该问题与弹簧加载类和代理的方式有关。它不会工作,直到您在另一个类中编写内部方法/事务或转到其他类,然后再次来到您的类,然后编写内部嵌套事务方法。
To summarize, spring proxies does not allow the scenarios which you are facing. you have to write the 2nd transaction method in other class
总而言之,spring 代理不允许您面临的场景。你必须在其他类中编写第二个事务方法
回答by Bunarro
Starting from Java 8 there's another possibility, which I prefer for the reasons given below:
从 Java 8 开始,还有另一种可能性,我更喜欢它,原因如下:
@Service
public class UserService {
@Autowired
private TransactionHandler transactionHandler;
public boolean addUsers(List<User> users) {
for (User user : users) {
transactionHandler.runInTransaction(() -> addUser(user.getUsername, user.getPassword));
}
}
private boolean addUser(String username, String password) {
// TODO
}
}
@Service
public class TransactionHandler {
@Transactional(propagation = Propagation.REQUIRED)
public <T> T runInTransaction(Supplier<T> supplier) {
return supplier.get();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public <T> T runInNewTransaction(Supplier<T> supplier) {
return supplier.get();
}
}
This approach has the following advantages:
这种方法有以下优点:
1) It may be applied to privatemethods. So you don't have to break encapsulation by making a method public just to satisfy Spring limitations.
1) 它可以应用于私有方法。因此,您不必为了满足 Spring 的限制而通过公开方法来破坏封装。
2) Same method may be called within different transaction propagation and it is up to the callerto choose the suitable one. Compare these 2 lines:
2)在不同的事务传播中可能会调用相同的方法,由调用者选择合适的方法。比较这两行:
transactionHandler.runInTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
transactionHandler.runInNewTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
3) It is explicit, thus more readable.
3)它是明确的,因此更具可读性。
回答by Mario Eis
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advised, as it may look strange to colleagues. But it works with singletons, is easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution as described in Espens answer.
以下是我为在同一类中仅少量使用方法调用的小型项目所做的工作。强烈建议使用代码中的文档,因为它对同事来说可能看起来很奇怪。但它适用于单例,易于测试,简单,快速实现,让我省去了完整的 AspectJ 工具。但是,对于更频繁的使用,我建议使用 Espens answer 中描述的 AspectJ 解决方案。
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {
private final PersonDao _personDao;
@Autowired
public PersonDao(PersonDao personDao) {
_personDao = personDao;
}
@Transactional
public void addUser(String username, String password) {
// call database layer
}
public void addUsers(List<User> users) {
for (User user : users) {
_personDao.addUser(user.getUserName, user.getPassword);
}
}
}