Spring, @Transactional и Hibernate Ленивый Загрузка - PullRequest
11 голосов
/ 17 ноября 2010

Я использую весну + спящий режим.Все мои HibernateDAO используют непосредственно sessionFactory.

У меня есть прикладной уровень -> сервисный уровень -> уровень DAO, и все коллекции загружаются лениво.

Итак, проблема в том, что когда-то на прикладном уровне (который содержит GUI / swing) я загружаю объект, используя метод сервисного уровня (который содержит аннотацию @Transactional), и я хочу использовать ленивое свойство этого объекта, но, очевидно, сессия уже закрыта.

Что такоелучший способ решить эту проблему?

EDIT

Я пытаюсь использовать 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 ]

3 голосов
/ 17 ноября 2010

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>
1 голос
/ 17 ноября 2010

К сожалению, вам нужно переделать управление сессиями.Это серьезная проблема при работе с Hibernate и Spring, и это гигантская проблема.

По сути, вам нужно, чтобы уровень вашего приложения создавал новый сеанс, когда он получает ваш объект Hibernate, и управлял этими закройте сессию должным образом.Этот материал хитрый и нетривиальный;Один из лучших способов справиться с этим - это посредничество сеансов через фабрику, доступную на уровне вашего приложения, но вам все еще нужно иметь возможность правильно завершить сеанс, поэтому вы должны знать о потребностях ваших данных в жизненном цикле.

Это самая распространенная жалоба на использование Spring и Hibernate таким образом;на самом деле, единственный способ справиться с этим - хорошо понять, каковы ваши жизненные циклы данных.

1 голос
/ 17 ноября 2010
  1. Создайте метод на уровне службы, который возвращает объект с отложенной загрузкой для этой сущности
  2. Перейдите к извлечению с нетерпением:)
  3. Если возможно, продлите транзакцию до уровня приложения

(пока мы ждем кого-то, кто знает, о чем они говорят)

...