Spring,@ Transactal和Hibernate Lazy Loading
我正在使用spring + hibernate。 我所有的HibernateDAO直接使用sessionFactory。
我有应用层 – >服务层 – > DAO层,所有集合都是懒惰加载的。
所以,问题是在应用程序层(包含GUI / swing)的某个时候我使用服务层方法(包含@Transactional注释)加载实体,我想使用这个对象的lazly属性,但是很明显会话已经关闭。
解决这个问题的最佳方法是什么?
编辑
我尝试使用MethodInterceptor,我的想法是为我的所有实体编写一个AroundAdvice并使用注释,例如:
// Custom annotation, say that session is required for this method @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SessionRequired { // An AroundAdvice to intercept method calls public class SessionInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation mi) throws Throwable { bool sessionRequired=mi.getMethod().isAnnotationPresent(SessionRequired.class); // Begin and commit session only if @SessionRequired if(sessionRequired){ // begin transaction here } Object ret=mi.proceed(); if(sessionRequired){ // commit transaction here } return ret; } } // An example of entity @Entity public class Customer implements Serializable { @Id Long id; @OneToMany List orders; // this is a lazy collection @SessionRequired public List getOrders(){ return orders; } } // And finally in application layer... public void foo(){ // Load customer by id, getCustomer is annotated with @Transactional // this is a lazy load Customer customer=customerService.getCustomer(1); // Get orders, my interceptor open and close the session for me... i hope... List orders=customer.getOrders(); // Finally use the orders }
你认为这可行吗? 问题是,如何在没有xml文件的情况下为我的所有实体注册这个拦截器? 有一种方法可以使用注释吗?
Hibernate最近推出了fetch配置文件,除了性能调优之外,它还是解决这类问题的理想选择。 它允许您(在运行时)在不同的加载和初始化策略之间进行选择。
编辑 (添加了关于如何使用拦截器设置获取配置文件的部分):
在开始之前:检查获取配置文件实际上是否适合您。 我自己没有使用它们,看到它们目前仅限于加入。 在浪费时间实现拦截器的实现和连接之前,请尝试手动设置获取配置文件并确保它实际上解决了您的问题。
有许多方法可以在Spring中设置拦截器(根据喜好),但最直接的方法是实现一个MethodInterceptor(参见http://static.springsource.org/spring/docs/3.0.x/spring- framework-reference / html / aop-api.html#aop-api-advice-around )。 让它拥有你想要的获取配置文件的setter和Hibernate Session工厂的setter:
public class FetchProfileInterceptor implements MethodInterceptor { private SessionFactory sessionFactory; private String fetchProfile; ... setters ... public Object invoke(MethodInvocation invocation) throws Throwable { Session s = sessionFactory.openSession(); // The transaction interceptor has already opened the session, so this returns it. s.enableFetchProfile(fetchProfile); try { return invocation.proceed(); } finally { s.disableFetchProfile(fetchProfile); } } }
最后,在Spring配置中启用拦截器。 这可以通过多种方式完成,您可能已经有一个可以添加的AOP设置。 请参见http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-schema 。
如果你是AOP的新手,我建议先尝试“旧的”ProxyFactory方式(http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop-api .html#aop-api-proxying-intf)因为它更容易理解它是如何工作的。 这里有一些示例XML可以帮助您入门:
... existingTransactionInterceptorBeanName fetchProfileInterceptor
- 在服务层中创建一个方法,该方法返回该实体的延迟加载对象
- 改为取得渴望:)
- 如果可能,将您的交易扩展到应用程序层
(就在我们等待有人知道他们在说什么的时候)
遗憾的是,您需要重新设计会话管理。 在处理Hibernate和Spring时,这是一个主要问题,这是一个巨大的麻烦。
从本质上讲,您需要的是您的应用程序层在获取Hibernate对象时创建新会话,并对其进行管理并正确关闭会话。 这个东西很棘手,而且非常重要; 管理此方法的最佳方法之一是通过应用程序层中提供的工厂来调解会话,但您仍需要能够正确结束会话,因此您必须了解数据的生命周期需求。
这个东西是以这种方式使用Spring和Hibernate的最常见的抱怨; 实际上,管理它的唯一方法就是准确掌握数据生命周期。
- 如何让Hibernate基于DDL / SQL脚本创建表?
- 无法解析org.springframework.transaction.annotation.Transactional的依赖项
- 如何使用Hibernate动态创建数据库中的表?
- 理解注释和JPA(hibernate)
- 相当于Grails中的InheritanceType.TABLE_PER_CLASS?
- Hibernate在HQL中遇到“@”字符有困难
- Java / Hibernate JPA:InheritanceType.TABLE_PER_CLASS和ID
- 在Spring JSP页面中使用集合时的Hibernate LazyInitializationException
- Hibernate + SQLite没有创建数据库