我正在使用Spring / JPA2 / hibernate代码:
class A {
@Autowired
B b;
@RequestMapping("/test")
public void test(final HttpServletRequest r1, HttpServletResponse r2) throws ... {
b.inner(); // Works
b.outer(); // javax.persistence.TransactionRequiredException:
// no transaction is in progress ... :|
}
@Component
class B {
@PersistenceContext
EntityManager em;
public void outer() { inner(); }
@Transactional
public void inner() { em.flush(); }
}
为什么inner()
仅在被调用时间接地松散了交易?
答案 0 :(得分:5)
在代理模式(默认设置)下,只拦截通过代理进入的外部方法调用。这意味着实际上,自调用目标对象中的一个方法调用目标对象的另一个方法,即使被调用的方法用@Transactional标记,也不会在运行时导致实际的事务。
如果您希望自我调用也包含在事务中,请考虑使用AspectJ模式(请参阅下表中的mode属性)。在这种情况下,首先不会有代理;相反,目标类将被编织(即,它的字节代码将被修改),以便将@Transactional转换为任何类型方法的运行时行为。
@Autowired
引用B b
(内部类A
)包含一个Spring AOP事务感知代理。
调用b.inner()
时,您将在事务感知实例上调用它,并将其标记为@Transactional
。因此,启动了Spring托管事务。
调用b.outer()
时,它也位于事务感知实例上,但不 @Transactional
。因此,Spring管理的交易不启动。
一旦您进入outer()
调用,inner()
的调用 通过事务感知代理,就会直接调用它。它与this.inner()
相同。由于您是直接调用它,而不是通过代理调用它,因此它没有Spring事务感知语义。
由于尚未启动任何交易,因此会产生TransactionRequiredException
。
可能的解决方案包括制作方法outer()
@Transactional
。
@Transactional
public void outer() { inner(); }
或制作整个班级@Transactional
。
@Transactional
@Component
class B {
@PersistenceContext
EntityManager em;
答案 1 :(得分:0)
事务上下文持续超过spring bean整个生命周期的范围。 @Transactional表示法具有整个组件的范围,您应该将@Component注释为@Transactional,例如
@Transactional
@Component
class B {
@PersistenceContext
EntityManager em;
public inner() { }
public outer() { }
}
内部和外部的方法应该完成单独的工作单元。如果你需要一些辅助函数或者你有什么好处,但是需要事务边界的工作单元应该作用于每个方法。请参阅@Transactional http://static.springsource.org/spring/docs/3.0.x/reference/transaction.html
上的spring文档