【问题标题】:Why does @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) fix Hibernate Lazy Loading Exceptions?为什么 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) 修复 Hibernate 延迟加载异常?
【发布时间】:2013-04-17 14:33:03
【问题描述】:

我将我正在开发的应用程序从使用 AspectJ 加载时间编织切换到使用 Spring CGlib 代理,在我这样做之后,我开始在代码的许多部分中出现休眠延迟加载异常,而在过去有没有抛出异常。

我已经能够通过将@Transactional(propagation = Propagation.SUPPORTS, readOnly = true) 添加到之前没有任何事务属性但调用 spring 存储库从数据库中读取数据的一组以前的公共方法来解决这些延迟加载异常。

任何人都知道为什么添加@Transactional(propagation = Propagation.SUPPORTS, readOnly = true) 会消除休眠延迟加载异常,以及为什么 AspectJ 加载时间编织不需要这些注释但在 out 时却需要这些注释?

更新 2 我认为删除 AspectJ 不是问题,但问题是我并不真正了解 SUPPORTS 传播的实际行为。特别是 SUPPORTS 如何与 JPA EntityManager 交互,因此我删除了一堆导致延迟加载异常的 SUPPORTS 传播。在阅读了 Spring Transaction Manager 的源代码之后,该做什么就变得很清楚了。 Spring 文档并没有很好地指出的关键思想是 @Transactional 注释用作将 EntityManager 的生命周期与事务方法的开始和结束联系起来的同步点。也强烈推荐这个系列文章http://www.ibm.com/developerworks/java/library/j-ts1/和这篇博文http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/

更新 1

这不是通过 AOP 代理调用私有 @Transactional 方法的情况。这些问题发生在从其他服务调用的公共方法中。

这是代码结构的示例,我在其中看到了问题的发生。

@Service
public class FooService 
{
   @Autowired
   private BarService barService;

   public void someMethodThatOnlyReads() {
      SomeResult result = this.barService.anotherMethodThatOnlyReads()

      // the following line blows up with a HibernateLazyLoadingEcxeption 
     // unless there is a @Transactional supports annotation on this method
      result.getEntity().followSomeRelationship(); 
    }

}

@Service
public class BarService 
{
   @Autowired
   private BarRepository barRepo;

   public SomeResult anotherMethodThatOnlyReads()
   {
      SomeEntity entity =  this.barRepo.findSomeEntity(1123);
      SomeResult result = new SomeResult();
      result.setEntity(entity);
      return result; 
    }
}

@Repository
public class BarRepository 
{
   @PersistenceContext
   private EntityManager em;

   public SomeEntity findSomeEntity(id Integer)
   {
      em.find(SomeEntity.class,id);
   }
}

【问题讨论】:

    标签: java spring hibernate


    【解决方案1】:

    我假设您的代码没有使用 OpenSessionInViewFilter 或类似的东西。

    没有@Transactional注解,Hibernate会话在离开BarRepository.findSomeEntity()方法后关闭。

    当调用 @Transactional 方法并且 TransactionalInterceptor 正确绑定到该方法(通过 cglib 代理或您在 Spring 上下文中拥有的任何其他 AOP 配置)时,Spring 会为完整的注释方法,从而防止任何延迟加载异常。

    如果您在 org.springframework.transactionorg.springframework.orm.hibernate3(或 hibernate4,如果您在 Hibernate 4 上)记录器上打开日志记录到 DEBUG,尤其是 HibernateTransactionManager 类和 org.springframework.transaction.support.AbstractPlatformTransactionManager,您应该看到正是在代码流中的哪个点,Spring 决定它需要打开和关闭 Hibernate Session。日志还应显示会话或事务在每个点打开/关闭的原因。

    【讨论】:

    • 打开调试并编写一些测试程序并阅读spring的源代码我消除了我对spring tx行为方式的所有误解,特别是SUPPORTS传播的行为,其中没有很好的记录主要的弹簧文档。
    • @ams 只是好奇,您了解到 SUPPORTS 的实际行为是什么,它与您最初的印象有何不同?
    • 我写了一个很不错的总结来回答这个问题stackoverflow.com/a/16557225/438319
    【解决方案2】:

    我不完全确定它为什么会发生,但我的理论如下。

    当您从 AspectJ 编织转移到 CGLIB 代理时,放置在从同一对象调用的方法上的 @Transactional 注释将停止生效。这意味着这些方法中的代码将以非事务方式执行(除非您的调用堆栈中有另一个 @Transacional 方法在其中 @Transacional 真正生效)。

    Javadoc for Propagation.SUPPORTS 说:

    注意:对于具有事务同步的事务管理器,PROPAGATION_SUPPORTS 与根本没有事务略有不同,因为它定义了同步将适用的事务范围。 因此,相同的资源(JDBC Connection、Hibernate Session 等)将在整个指定范围内共享。请注意,这取决于事务管理器的实际同步配置。

    因此,当您的代码以非事务方式执行时,用于加载对象的 Hibernate Session 将无法用于随后的惰性属性初始化。当您使用 @Transactional(propagation = Propagation.SUPPORTS) 注释代码堆栈中的顶级方法时,Hibernate Session 将可用,直到您离开该方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-13
      • 2011-05-11
      • 2017-11-08
      • 1970-01-01
      • 2020-05-16
      • 2012-05-21
      • 1970-01-01
      • 2021-02-21
      相关资源
      最近更新 更多