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

2022-09-02 21:59:20

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

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

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

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

更新 1

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

下面是代码结构的一个示例,我看到问题发生。

@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);
   }
}

答案 1

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

如果没有注释,休眠会话将在离开方法后关闭。@TransactionalBarRepository.findSomeEntity()

当调用一个方法并且 将 正确绑定到该方法(通过 cglib 代理或您在 Spring 上下文中拥有的任何其他 AOP 配置)时,则 Session 将由 Spring 在整个带注释的方法中保持打开状态,从而防止任何延迟加载异常。@TransactionalTransactionalInterceptor

如果您将日志记录打开到 和(或者如果您使用的是 Hibernate 4)记录器,特别是类和 ,您应该确切地看到 Spring 在代码流中的哪些点决定需要打开和关闭休眠会话。日志还应显示会话或事务在每个点打开/关闭的原因。DEBUGorg.springframework.transactionorg.springframework.orm.hibernate3hibernate4HibernateTransactionManagerorg.springframework.transaction.support.AbstractPlatformTransactionManager


答案 2

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

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

Javadoc for Propagation.SUPPORTS 说:

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

因此,当代码以非事务方式执行时,用于加载对象的休眠将不可用于延迟属性的后续初始化。当您使用 对代码堆栈中的顶级方法进行批注时,休眠将一直可用,直到您离开该方法。Session@Transactional(propagation = Propagation.SUPPORTS)Session


推荐