Подтвердить что ты не робот

Spring, @Transactional и Hibernate Lazy Загрузка

Я использую spring + hibernate. Все мои HibernateDAO используют непосредственно sessionFactory.

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

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

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

ИЗМЕНИТЬ

Я пытаюсь использовать 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 файле? Есть способ сделать это с помощью аннотации?

4b9b3361

Ответ 1

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

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. Это можно сделать несколькими способами, и у вас, вероятно, уже есть настройка АОП, к которой вы можете добавить. См. 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>

Ответ 2

  • Создайте метод на уровне сервиса, который возвращает объект с леними нагрузками для этого объекта
  • Измените выборку:
  • Если возможно, переведите транзакцию в прикладной уровень

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

Ответ 3

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

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

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