Spring,@Transactional和Hibernate惰性加载 [英] Spring, @Transactional and Hibernate Lazy Loading

查看:79
本文介绍了Spring,@Transactional和Hibernate惰性加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用spring + hibernate。我所有的HibernateDAO都直接使用sessionFactory。



我有应用层 - >服务层 - > DAO层,并且所有集合都被加载。 b $ b

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



解决这个问题的最好方法是什么?



编辑



我尝试使用MethodInterceptor,我的想法是为我的所有实体编写AroundAdvice并使用注释,例如:

  //自定义注释,比如说这个方法需要session 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SessionRequired {


// AroundAdvice截获方法调用
public class SessionInterceptor im plements MethodInterceptor {

public Object invoke(MethodInvocation mi)throws Throwable {
bool sessionRequired = mi.getMethod()。isAnnotationPresent(SessionRequired.class);
//只有在@SessionRequired
if(sessionRequired){
// begin transaction here
}
Object ret = mi.proceed();
if(sessionRequired){
// commit transaction here
}
return ret;



//实体示例
@Entity
public class Customer实现Serializable {

@Id
长ID;

@OneToMany
列表< Order>命令; //这是一个懒惰的集合

@SessionRequired
public List< Order> getOrders(){
返回订单;



//最后在应用层...
public void foo(){
//通过id载入客户,getCustomer用@Transactional
注解//这是一个懒加载
Customer customer = customerService.getCustomer(1);

//获取订单,我的拦截器为我打开并关闭会话...我希望...
List< Order>订单= customer.getOrders();

//最后使用订单
}

你认为可以这样工作吗?
问题是,如何在不使用xml文件的情况下为所有实体注册此拦截器?
有注解的方法吗?

解决方案

Hibernate最近引入了获取配置文件,除了性能调谐)对于解决这样的问题非常理想。它允许你(在运行时)选择不同的加载和初始化策略。

3.5 / reference / en / html / performance.html#performance-fetching-profilesrel =nofollow> 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的建议,各地)。让它有一个你需要的获取配置文件的setter和Hibernate Session工厂的setter:

public $ FetchProfileInterceptor implements MethodInterceptor {

private SessionFactory sessionFactory;
private String fetchProfile;
$ b $ ... setters ...

public Object invoke(MethodInvocation invocation)throws Throwable {
Session s = sessionFactory.openSession(); //事务拦截器已经打开会话,所以它返回它。
s.enableFetchProfile(fetchProfile);
尝试{
return invocation.proceed();
} finally {
s.disableFetchProfile(fetchProfile);


$ b $ / code>

最后,启用拦截器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 =fetchProfileInterceptorclass =x.y.zFetchProfileInterceptor> 
< property name =sessionFactoryref =sessionFactory/>
< property name =fetchProfileref =gui-profile/>
< / bean>

< bean id =businessServiceclass =x.y.x.BusinessServiceImpl>
< property name =dao... />
...
< / bean>

< bean id =serviceForSwinGUI
class =org.springframework.aop.framework.ProxyFactoryBean>
< property name =proxyInterfacesvalue =xyzBusinessServiceInterface />

< property name =targetref =businessService/>
< property name =interceptorNames>
< list>
< value> existingTransactionInterceptorBeanName< /值>
<值> fetchProfileInterceptor< /值>
< / list> ;
< / property>
< / bean>


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

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

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.

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:

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

解决方案

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.

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:

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.

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:

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

这篇关于Spring,@Transactional和Hibernate惰性加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆