Java Spring、@Transactional和Hibernate延迟加载
我正在使用spring+hibernate。我所有的HibernateDAO都直接使用sessionFactory 我有应用层->服务层->DAO层,所有集合都是lazly加载的 所以,问题是,在应用层(包含GUI/swing)的某个时候,我使用服务层方法(包含@Transactional注释)加载了一个实体,我想使用这个对象的lazly属性,但显然会话已经关闭 解决这个问题的最好办法是什么 编辑 我尝试使用MethodInterceptor,我的想法是为我的所有实体编写一个AroundAdvice并使用注释,例如:Java Spring、@Transactional和Hibernate延迟加载,java,hibernate,spring,lazy-loading,Java,Hibernate,Spring,Lazy Loading,我正在使用spring+hibernate。我所有的HibernateDAO都直接使用sessionFactory 我有应用层->服务层->DAO层,所有集合都是lazly加载的 所以,问题是,在应用层(包含GUI/swing)的某个时候,我使用服务层方法(包含@Transactional注释)加载了一个实体,我想使用这个对象的lazly属性,但显然会话已经关闭 解决这个问题的最好办法是什么 编辑 我尝试使用MethodInterceptor,我的想法是为我的所有实体编写一个AroundAdvi
// 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
}
//自定义注释,假设此方法需要会话
@目标(ElementType.METHOD)
@保留(RetentionPolicy.RUNTIME)
public@interface SessionRequired{
//拦截方法调用的AroundAdvice
公共类SessionInterceptor实现MethodInterceptor{
公共对象调用(MethodInvocation mi)抛出可丢弃的{
bool sessionRequired=mi.getMethod().isAnnotationPresent(sessionRequired.class);
//仅当@SessionRequired时才开始并提交会话
如果需要(会话){
//在这里开始交易
}
Object ret=mi.procedure();
如果需要(会话){
//在此处提交事务
}
返回ret;
}
}
//实体的一个例子
@实体
公共类Customer实现了可序列化{
@身份证
长id;
@独身癖
列出顺序;//这是一个惰性集合
@会议要求
公共列表getOrders(){
退货订单;
}
}
//最后在应用层。。。
公共图书馆{
//按id加载客户,getCustomer用@Transactional注释
//这是一个懒惰的负载
Customer=customerService.getCustomer(1);
//获取命令,我的截取器为我打开和关闭会话…我希望。。。
List orders=customer.getOrders();
//最后使用命令
}
你认为这行吗?
问题是,如何为我的所有实体注册这个拦截器,而不在xml文件中注册它?
有一种方法可以通过注释来实现吗
(就在我们等待知道他们在说什么的人时)不幸的是,您需要重新设计会话管理。这是处理Hibernate和Spring时的一个主要问题,这是一个巨大的麻烦 本质上,您需要的是应用程序层在获取Hibernate对象时创建一个新会话,并对其进行管理并正确关闭会话。这是一件棘手的事情;管理这件事的最佳方法之一是通过应用程序层提供的工厂来调解会话,但您仍然需要为了能够正确结束会话,您必须了解数据的生命周期需求
这是关于以这种方式使用Spring和Hibernate的最常见的抱怨;实际上,管理它的唯一方法是正确地处理数据生命周期。Hibernate最近引入了fetch配置文件,它(除了性能调优)非常适合解决此类问题。它允许您(在运行时)执行选择不同的加载和初始化策略 编辑(添加了关于如何使用拦截器设置获取配置文件的部分): 开始之前:检查fetch配置文件是否适合您。我自己没有使用过它们,并且看到它们当前仅限于加入fetch。在您浪费时间实现和连接拦截器之前,请尝试手动设置fetch配置文件,并确保它确实解决了您的问题 在Spring中设置拦截器的方法有很多(根据偏好),但最直接的方法是实现MethodInterceptor(请参阅)。让它有一个用于所需获取配置文件的setter和用于Hibernate会话工厂的setter:
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设置。请参阅
如果您是AOP新手,我建议您先尝试“旧的”代理工厂方法(http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html#aop-api代理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.有点无聊,每次我想加载一个惰性对象时我都要写一个方法,2.性能太过膨胀。也许3.解决方案是……我可以在每次使用spring AOP命中一个惰性对象时创建一个会话吗?@blow:可以,这是一个非常好的主意;问题是知道何时终止会话(即,当对象修改完成时)。在某些情况下,这很容易;在另一些情况下,这很难。@Daggerz:这没有用,在DAO中有两个方法,如loadLazly和LoadEarge,并使用一个获取配置文件有什么区别?没有区别,如果我需要加载一个延迟加载的特定对象,我必须编写不同的方法。例如,你可以包装服务/dao带有一个拦截器,在从gui调用时设置获取配置文件“gui”。如果您需要更多详细信息,请告诉我。谢谢Daggerz,我有一个简单的问题:目前我使用自动扫描来扫描我的组件(dao、srvices存储库等),我可以为我的所有实体自动注册一个拦截器吗?我将为此问题编辑我的第一篇文章。有关AOP,请参阅。它不使用MethodInterceptor,但如果您使用AspectJ和纯注释,则不必使用该拦截器。有关您在编辑中实现的拦截器的注释: