我正在使用Spring + Hibernate。我所有的HibernateDAO都直接使用sessionFactory。
我有应用程序层->服务层-> DAO层,所有集合都被缓慢加载。
因此,问题在于,有时在应用程序层(包含GUI / swing)中,我会使用服务层方法(包含@Transactional批注)加载实体,并且我想使用此对象的惰性属性,但忽略了会话已经关闭。
解决此问题的最佳方法是什么?
编辑
我尝试使用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<Order> orders; // this is a lazy collection @SessionRequired public List<Order> 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<Order> orders=customer.getOrders(); // Finally use the orders }
您认为这行得通吗?问题是,如何为我所有的实体注册此拦截器,而不在xml文件中进行注册?有办法用注解吗?
Hibernate最近推出了获取配置文件(除了性能调整之外)对于解决此类问题也是理想的选择。它允许您(在运行时)在不同的加载和初始化策略之间进行选择。
http://docs.jboss.org/hibernate/core/3.5/reference/en/html/performance.html#performance- fetching- profiles
编辑 (添加了有关如何使用拦截器设置获取配置文件的部分):
开始之前:检查获取配置文件实际上是否对您有用。我还没有亲自使用过它们,而是看到它们目前仅限于加入访存。在浪费时间实现和连接拦截器之前,请尝试手动设置获取配置文件,以查看它实际上可以解决您的问题。
有很多方法可以在Spring中设置拦截器(根据偏好),但是最直接的方法是实现MethodInterceptor(请参阅http://static.springsource.org/spring/docs/3.0.x/spring- framework-reference / html / aop-api.html#aop-api-advice- around)。让它具有所需的获取配置文件的设置器和Hibernate Session工厂的设置器:
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,可以帮助您入门:
<bean id="fetchProfileInterceptor" class="x.y.zFetchProfileInterceptor"> <property name="sessionFactory" ref="sessionFactory"/> <property name="fetchProfile" ref="gui-profile"/> </bean> <bean id="businessService" class="x.y.x.BusinessServiceImpl"> <property name="dao" .../> ... </bean> <bean id="serviceForSwinGUI" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="x.y.z.BusinessServiceInterface/> <property name="target" ref="businessService"/> <property name="interceptorNames"> <list> <value>existingTransactionInterceptorBeanName</value> <value>fetchProfileInterceptor</value> </list> </property> </bean>