[英]Indirect Hibernate/JPA method call loses transaction
我正在使用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()
只在被调用时间接地松散了事务?
在代理模式(默认设置)下,只拦截通过代理进入的外部方法调用。 这意味着实际上,自调用目标对象中的一个方法调用目标对象的另一个方法,即使被调用的方法用@Transactional标记,也不会在运行时导致实际的事务。
如果您希望自我调用也包含在事务中,请考虑使用AspectJ模式(请参阅下表中的mode属性)。 在这种情况下,首先不会有代理; 相反,目标类将被编织(即,它的字节代码将被修改),以便在任何类型的方法上将@Transactional转换为运行时行为。
@Autowired
引用B b
(在A
类中)包含一个Spring AOP事务感知代理。
当b.inner()
,您在事务感知实例上调用它,并将其标记为@Transactional
。 因此,启动了Spring托管事务。
当b.outer()
,它也在事务感知实例上,但它不是 @Transactional
。 因此,Spring管理的事务未启动。
一旦你进入outer()
的调用,对inner()
outer()
的调用就不会通过事务感知代理,而是直接调用它。 它与this.inner()
相同。 由于您是直接调用它,而不是通过代理调用它,因此它没有Spring事务感知语义。
由于没有启动任何事务,因此会导致TransactionRequiredException
。
可能的解决方案包括使方法outer()
@Transactional
。
@Transactional
public void outer() { inner(); }
或者制作整个班级@Transactional
。
@Transactional
@Component
class B {
@PersistenceContext
EntityManager em;
事务上下文持续超过spring bean整个生命周期的范围。 @Transactional表示法具有整个组件的范围,您应该将@Component注释为@Transactional,例如
@Transactional
@Component
class B {
@PersistenceContext
EntityManager em;
public inner() { }
public outer() { }
}
内部和外部的方法应该完成单独的工作单元。 如果你需要一些辅助函数或者你有什么好处,但是需要事务边界的工作单元应该作用于每个方法。 请参阅@Transactional上的spring文档http://static.springsource.org/spring/docs/3.0.x/reference/transaction.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.