我将使用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-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);
}
}
答案 0 :(得分:9)
我认为您的代码未使用OpenSessionInViewFilter
或类似内容。
没有@Transactional
注释,离开BarRepository.findSomeEntity()
方法后Hibernate会话就会关闭。
当调用@Transactional
方法并且TransactionalInterceptor
正确绑定到方法时(通过cglib代理或Spring上下文中的任何其他AOP配置),会话保持打开状态通过Spring获取整个带注释的方法,从而防止任何延迟加载异常。
如果您将日志记录调到DEBUG
上的org.springframework.transaction
和org.springframework.orm.hibernate3
(或hibernate4
,如果您使用的是Hibernate 4)记录器,尤其是{{1} } class和HibernateTransactionManager
,你应该确切地看到代码流中的哪些点Spring决定它需要打开和关闭Hibernate Session。日志还应显示在每个点打开/关闭会话或事务的原因。
答案 1 :(得分:8)
我不完全确定它为什么会发生,但我的理论如下。
当您从AspectJ编织移动到CGLIB代理时,放置在从同一对象调用的方法上的@Transactional
注释将停止生效。这意味着这些方法中的代码将以非事务方式执行(除非您的调用堆栈中有另一个@Transacional
方法@Transacional
真正生效)。
Javadoc for Propagation.SUPPORTS
说:
注意:对于具有事务同步的事务管理器,PROPAGATION_SUPPORTS与根本没有事务略有不同,因为它定义了同步将应用的事务范围。 因此,将为整个指定范围共享相同的资源(JDBC连接,Hibernate会话等)。请注意,这取决于事务管理器的实际同步配置。
因此,当您的代码以非事务方式执行时,用于加载对象的Hibernate Session
将无法用于后续初始化延迟属性。当您使用@Transactional(propagation = Propagation.SUPPORTS)
在代码堆栈中注释顶级方法时,Hibernate Session
将一直可用,直到您离开该方法。