java Spring、@Transactional 和 Hibernate 延迟加载

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4207510/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 05:18:54  来源:igfitidea点击:

Spring, @Transactional and Hibernate Lazy Loading

javahibernatespringlazy-loading

提问by blow

i'm using spring + hibernate. All my HibernateDAO use directly sessionFactory.

我正在使用弹簧 + 休眠。我所有的 HibernateDAO 都直接使用 sessionFactory。

I have application layer -> service layer -> DAO layer and all collections is lazly loaded.

我有应用层 -> 服务层 -> DAO 层,所有集合都是延迟加载的。

So, the problem is that sometime in the application layer(that contains GUI/swing) i load an entity using a service layer method(that contains @Transactional annotation) and i want to use a lazly property of this object, but obviusly the session is already closed.

所以,问题是在应用层(包含 GUI/swing)的某个时候,我使用服务层方法(包含 @Transactional 注释)加载一个实体,我想使用这个对象的惰性属性,但显然会话已经关闭。

What is the best way to resolve this trouble?

解决此问题的最佳方法是什么?

EDIT

编辑

I try to use a MethodInterceptor, my idea is to write an AroundAdvice for all my Entities and use annotation, so for example:

我尝试使用 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
}

Do you think can this work? The problem is, how to register this interceptor for all my entities without do it in xml file? There is a way to do it with annotation?

你认为这行得通吗?问题是,如何在 xml 文件中为我的所有实体注册这个拦截器?有没有办法用注释来做到这一点?

采纳答案by DaGGeRRz

Hibernate recently introduced fetch profiles which (in addition to performance tuning) is ideal for solving issues like this. It allows you to (at runtime) choose between different loading and initialization strategies.

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

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

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

Edit(added section on how to set the fetch profile using an interceptor):

编辑(添加了关于如何使用拦截器设置提取配置文件的部分):

Before you get started: Check that fetch profiles actually will work for you. I haven't used them myself and see that they are currently limited to join fetches. Before you waste time on implementing and wiring up the interceptor, try setting the fetch profile manually and see that it actually solves your problem.

开始之前:检查获取配置文件实际上是否适合您。我自己没有使用过它们,并且看到它们目前仅限于加入提取。在您浪费时间实现和连接拦截器之前,请尝试手动设置提取配置文件并查看它是否确实解决了您的问题。

There are many ways to setup interceptors in Spring (according to preference), but the most straight-forward way would be to implement a MethodInterceptor (see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html#aop-api-advice-around). Let it have a setter for the fetch profile you want and setter for the Hibernate Session factory:

在 Spring 中设置拦截器的方法有很多(根据偏好),但最直接的方法是实现 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);
        }
    }
}

Lastly, enable the interceptor in the Spring config. This can be done in several ways and you probably already have a AOP setup that you can add it to. See http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-schema.

最后,在 Spring 配置中启用拦截器。这可以通过多种方式完成,您可能已经有了可以将其添加到的 AOP 设置。请参阅http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-schema

If you're new to AOP, I'd suggest trying the "old" ProxyFactory way first (http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html#aop-api-proxying-intf) because it's easier to understand how it works. Here's some sample XML to get you started:

如果您是 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>

回答by willcodejavaforfood

  1. Create a method in the service layer that returns the lazy-loaded object for that entity
  2. Change to fetch eager :)
  3. If possible extend your transaction into the application layer
  1. 在服务层创建一个方法,为该实体返回延迟加载的对象
  2. 更改为 fetch 热切:)
  3. 如果可能,将您的交易扩展到应用层

(just while we wait for someone who knows what they are talking about)

(就在我们等待知道他们在说什么的人的时候)

回答by Paul Sonier

You need to rework your session management, unfortunately. This is a major problem when dealing with Hibernate and Spring, and it's a gigantic hassle.

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

Essentially, what you need is for your application layer to create a new session when it gets your Hibernate object, and to manage that and close the session properly. This stuff is tricky, and non-trivial; one of the best ways to manage this is to mediate the sessions through a factory available from your application layer, but you still need to be able to end the session properly, so you have to be aware of the lifecycle needs of your data.

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

This stuff is the most common complaint about using Spring and Hibernate in this way; really, the only way to manage it is to get a good handle on exactly what your data lifecycles are.

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