2010-11-17 3 views
11

Я использую spring + hibernate. Все мои HibernateDAO используют непосредственно sessionFactory.Spring, @Transactional и Hibernate Lazy Загрузка

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

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

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

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

Hibernate недавно представил профили извлечения, которые (в дополнение к настройке производительности) идеально подходят для решения таких проблем. Он позволяет (во время выполнения) выбирать между различными стратегиями загрузки и инициализации.

http://docs.jboss.org/hibernate/core/3.5/reference/en/html/performance.html#performance-fetching-profiles

Редактировать (добавлен раздел о том, как установить выборки профиля с помощью перехватчик):

Перед тем, как приступить к работе: Убедитесь, что выборки профилей на самом деле будет работать для вас. Я сам их не использовал и вижу, что в настоящее время они ограничены, чтобы присоединиться к выборкам. Прежде чем тратить время на внедрение и подключение перехватчика, попробуйте вручную установить профиль извлечения и убедитесь, что он фактически решает вашу проблему.

Существует множество способов установки перехватчиков весной (в соответствии с предпочтением), но самым прямым способом было бы реализовать MethodInterceptor (см. http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html#aop-api-advice-around). Пусть у сеттера для выборки профиля вы хотите, и сеттер для сессии завода Hibernate:

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); 
     } 
    } 
} 

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

@DaGGeRRz: это не полезно, в чем разница, чтобы иметь два метода в DAO, например loadLazly и loadEager, и использовать профиль fetch? Нет никакой разницы, я должен написать другой метод, если мне нужно загрузить определенный объект, который ленивый загружен. – blow

+1

Вы можете, например, обернуть службу/dao перехватчиком, который задает профиль профиля «gui» при вызове из gui. Дайте мне знать, если вам нужна дополнительная информация об этом. – DaGGeRRz

+0

@DaGGeRRz: конечно, спасибо, этот звук интересный. – blow

1
  1. Создать метод в слое службы, который возвращает ленивый загруженный объект для этого объекта
  2. Изменения принести нетерпеливыми :)
  3. Если возможно продлить сделку в прикладном уровень

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

+0

спасибо, 1. немного скучный, я должен написать метод каждый раз, когда я хочу загрузить ленивый объект, 2. слишком экспансивный в Perfomance. Может быть, 3. это решение ... – blow

1

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

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

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

+0

Могу ли я создать сессию каждый раз, когда я нажму на ленивый объект, используя Spring AOP? – blow

+0

@ blow: да, вы можете, и это очень хорошая идея; проблема становится зная, когда прекращать сеанс (т. е. когда ваша модификация объекта завершена). В некоторых случаях это легко; в других, удивительно тяжело. –

Смежные вопросы