【问题标题】:Indirect Hibernate/JPA method call loses transaction间接 Hibernate/JPA 方法调用丢失事务
【发布时间】:2011-09-23 21:14:20
【问题描述】:

我正在使用 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() 仅在间接调用时才会丢失事务?

【问题讨论】:

    标签: java hibernate spring jpa-2.0


    【解决方案1】:

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

    在代理模式下(默认),只有通过代理传入的外部方法调用会被拦截。这意味着自调用,实际上是目标对象中的一个方法调用目标对象的另一个方法,在运行时不会导致实际的事务,即使调用的方法被标记为@Transactional。

    如果您希望自调用也包含在事务中,请考虑使用 AspectJ 模式(参见下表中的模式属性)。在这种情况下,首先不会有代理;相反,目标类将被编织(即,其字节码将被修改)以便将@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;
    

    【讨论】:

    • 我的目标是创建一个后台程序并每次都提交一些小代码。因此,将 outer() 注释为单一事务并不是我的意图。我将代理模式更改为 aspect-j 模式。谢谢!
    • 如果B.outer() 中的逻辑需要在B.inner() 之前发生但不涉及事务/持久性,它可能不属于你的持久层类(DAL/DAO 等)。考虑重构您的设计以分离关注点,可能通过使用中间对象。 Object intermediate = b.outer(); b.inner(intermediate);。然后重构,使它不再是你的持久层类的成员。 Object intermediate = someOtherUtilityOrSupportClassInstance.outer(); b.inner(intermediate);.
    • 我现在已经切换到 LTW,但我现在在任何调用中都没有收到任何事务:( 添加了所有配置 , stackoverflow.com/questions/2536292/…" load-time weaver 不应该与 Hibernate 一起使用,这是用于 Toplink)" 或者我需要编译时编织吗?
    • 我个人从未与 LTW 合作过(因为限制性的安全要求和使用 java 代理/仪器),所以需要其他人加入,或者您应该提出一个新问题。从派生示例中我仍然不清楚,为什么您的设计不应该使用代理/拦截器方法;只要您适当地重构您的事务范围,或者删除看起来(至少从示例中)看起来是代码气味的嵌套事务代码。
    【解决方案2】:

    事务上下文会在 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 文档

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-10
      • 2021-08-28
      • 1970-01-01
      • 2016-08-04
      • 2019-05-17
      • 2019-03-29
      • 2011-06-26
      相关资源
      最近更新 更多