间接Hibernate / JPA方法调用丢失事务

时间:2011-06-27 22:21:50

标签: java hibernate spring jpa-2.0

我正在使用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()仅在被调用时间接地松散了交易?

2 个答案:

答案 0 :(得分:5)

http://static.springsource.org/spring/docs/current/reference/transaction.html#transaction-declarative-annotations

  

在代理模式(默认设置)下,只拦截通过代理进入的外部方法调用。这意味着实际上,自调用目标对象中的一个方法调用目标对象的另一个方法,即使被调用的方法用@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文档