我将正在开发的应用程序从使用AspectJ Load Time Weaving切换到使用Spring CGlib代理,然后,在以前没有抛出任何异常的情况下,代码的许多部分开始出现Hibernate延迟加载异常。
我已经通过在以前没有事务属性但调用Spring仓库从数据库读取数据的一些公共方法上添加@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
来解决这些延迟加载异常。
有人知道为什么添加@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
会消除Hibernate延迟加载异常,以及为什么这些注释在AspectJ Load Time Weaving中不需要但在没有AspectJ时需要吗?
更新2 我相信删除AspectJ并不是问题所在,问题在于我并没有真正理解SUPPORTS传播的实际行为。特别是SUPPORTS如何与JPA EntityManager交互,因此我删除了一堆SUPPORTS传播,这导致了延迟加载异常。阅读Spring事务管理器的源代码后,所有问题都变得清晰明了。关键思想是,Spring文档并没有很好地指出@Transactional注释是作为同步点使用的,将EntityManager的生命周期与事务性方法的开始和结束联系起来。也强烈推荐查看http://www.ibm.com/developerworks/java/library/j-ts1/中的这一系列文章和此博客文章。
更新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);
}
}