我有一个数据访问类,它作为独立java应用程序的一部分运行。它目前正在工作,这意味着定义了一个事务管理器,但我想重构该类以减少事务的范围,但如果我这样做,我得到 org.hibernate.HibernateException:没有Hibernate Session绑定到线程,并且配置这里不允许创建非事务性的,这意味着移动 @Transactional 会以某种方式阻止它被识别。
我的原始版本将重构的方法设为私有,但我发现建议将其更改为公开,因为在某些情况下注释不会被选中。
public class DoStuff {
@Transactional
public void originalMethod() {
// do database stuff
...
// do non-database stuff that is time consuming
...
}
}
我想要做的是重构以下
public class DoStuff {
public void originalMethod() {
doDatabaseStuff()
doNonDatabaseStuff()
}
@Transactional
public void doDatabaseStuff() {
...
}
public void doNonDatabaseStuff() {
...
}
}
答案 0 :(得分:7)
修改强>
您需要了解how Spring proxying works才能理解为什么您的重构不起作用。
对象引用的方法调用将是代理上的调用,因此代理将能够委托给与该特定方法调用相关的所有拦截器(通知)。但是,一旦调用最终到达目标对象,就会调用它可能对其自身进行的任何方法调用,而不是针对此引用调用,而不是代理。这具有重要意义。这意味着自我调用不会导致与方法调用相关的建议有机会执行。
@Transactional使用Spring AOP,Spring使用代理。这意味着当您从另一个类调用@Transactional方法时,Spring将使用代理,因此将应用事务建议。但是,如果从同一个类调用该方法,spring将使用“this”引用而不是代理,因此不会应用事务性建议。
原始答案:
在类似的情况下,这对我有用。
public class DoStuff implement ApplicationContextAware {
private ApplicationContext CONTEXT;
public void setApplicationContext(ApplicationContext context) throws BeansException {
CONTEXT = context;
}
public void originalMethod() {
getSpringProxy().doDatabaseStuff()
doNonDatabaseStuff()
}
private DoStuff getSpringProxy() {
return context.getBean(this.getClass());
}
@Transactional
public void doDatabaseStuff() {
...
}
public void doNonDatabaseStuff() {
...
}
}
说明:
ApplicationContextAware
,因此它引用了上下文答案 1 :(得分:0)
你的方法看起来应该可以正常工作,我希望这个问题与Spring代理有关。
我询问接口的原因与Spring应用事务行为的默认方法有关 - JDK动态代理。
如果您班级的实际定义是:
public class DoStuff implements Doable {
public void originalMethod() {
}
}
public interface Doable {
public void originalMethod();
}
如果这确实是结构,当你转移到新结构时,Spring无法代理新的doDatabaseStuff
方法。
您可以选择解决此问题: