Spring,@ Transactal和Hibernate Lazy Loading

时间:2010-11-17 17:55:00

标签: java hibernate spring 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<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文件的情况下为我的所有实体注册这个拦截器? 有一种方法可以使用注释吗?

3 个答案:

答案 0 :(得分:3)

Hibernate最近推出了fetch配置文件,除了性能调优之外,它还是解决这类问题的理想选择。它允许您(在运行时)在不同的加载和初始化策略之间进行选择。

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)。让它拥有你想要的获取配置文件的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可以帮助您入门:

<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>

答案 1 :(得分:1)

  1. 在服务层中创建一个返回该实体的延迟加载对象的方法
  2. 更改为获取热切:)
  3. 如果可能,将您的交易扩展到应用层
  4. (就在我们等待有人知道他们在说什么的时候)

答案 2 :(得分:1)

不幸的是,您需要重新设计会话管理。在处理Hibernate和Spring时这是一个主要问题,这是一个巨大的麻烦。

基本上,您需要的是您的应用程序层在获取Hibernate对象时创建新会话,并对其进行管理并正确关闭会话。这个东西很棘手,而且非常重要;管理此问题的最佳方法之一是通过应用程序层提供的工厂来调解会话,但您仍需要能够正确结束会话,因此您必须了解数据的生命周期需求。 / p>

这种东西是以这种方式使用Spring和Hibernate最常见的抱怨;实际上,管理它的唯一方法就是准确掌握你的数据生命周期。