为什么@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)修复了Hibernate延迟加载异常?

ams*_*ams 17 java spring hibernate

我使用AspectJ加载时间编织切换到我正在使用的应用程序,使用Spring CGlib代理,在我做了之后,我开始得到hibernate延迟加载异常的代码的许多部分,在过去没有例外是抛出.

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

任何人都知道为什么添加会@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)消除hibernate延迟加载异常以及为什么这些注释不需要使用AspectJ加载时间编织但是是否需要输出?

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

更新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);
   }
}
Run Code Online (Sandbox Code Playgroud)

mat*_*t b 9

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

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

当一个@Transactional方法被调用并且TransactionalInterceptor正确绑定到该方法时(通过cglib代理或Spring上下文中的任何其他AOP配置),Spring会为整个注释方法保持打开Session,从而防止任何延迟加载异常.

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

  • 我写了一篇非常好的总结来回答这个问题http://stackoverflow.com/a/16557225/438319 (2认同)

axt*_*avt 8

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

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

JavadocPropagation.SUPPORTS说:

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

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