J2EE进阶(八)Hibernate与延迟加载机制探究
Hibernate与延迟加载机制探究
前言
Hibernate对象关系映射提供延迟的与非延迟的对象初始化。非延迟加载在读取一个对象的时候会将与这个对象所有相关的其他对象一起读取出来。这有时会导致成百的(如果不是成千的话)select语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候,经常会导致整个数据库都在初始化的阶段被读出来了。当然,你可以不厌其烦地检查每一个对象与其他对象的关系,并把那些最昂贵的删除,但是到最后,我们可能会因此失去了本想在ORM工具中获得的便利。
一个明显的解决方法是使用Hibernate提供的延迟加载机制。这种初始化策略只在一个对象调用它的一对多或多对多关系时才将关系对象读取出来。这个过程对开发者来说是透明的,而且只进行了很少的数据库操作请求,因此会得到比较明显的性能提升。这项技术的一个缺陷是延迟加载技术要求一个Hibernate会话要在对象使用的时候一直开着。这会成为通过使用DAO模式将持久层抽象出来时的一个主要问题。为了将持久化机制完全地抽象出来,所有的数据库逻辑,包括打开或关闭会话,都不能在应用层出现。最常见的是,一些实现了简单接口的DAO实现类将数据库逻辑完全封装起来了。一种快速但是笨拙的解决方法是放弃DAO模式,将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效,但是在大的系统中,这是一个严重的设计缺陷,妨碍了系统的可扩展性。
Web层进行延迟加载
幸运的是,Spring框架为Hibernate延迟加载与DAO模式的整合提供了一种方便的解决方法。对那些不熟悉Spring与Hibernate集成使用的人,我不会在这里讨论过多的细节,但是我建议你去了解Hibernate与Spring集成的数据访问。以一个Web应用为例,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我们可以随意选择一个类来实现相同的功能。两种方法唯一的不同就在于interceptor在Spring容器中运行并被配置在web应用的上下文中,而Filter在Spring之前运行并被配置在web.xml中。不管用哪个,他们都在请求将当前会话与当前(数据库)线程绑定时打开Hibernate会话。一旦已绑定到线程,这个打开了的Hibernate会话可以在DAO实现类中透明地使用。这个会话会为延迟加载数据库中值对象的视图保持打开状态。一旦这个逻辑视图完成了,Hibernate会话会在Filter的doFilter方法或者Interceptor的postHandle方法中被关闭。下面是每个组件的配置示例:
Interceptor的配置
<beans> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="openSessionInViewInterceptor"/> </list> </property> <property name="mappings"> </bean> <bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor"> <property name="sessionFactory"><ref bean="sessionFactory"/></property> </bean> </beans>
Filter的配置
<web-app> <filter> <filter-name>hibernateFilter</filter-name> <filter-class> org.springframework.orm.hibernate.support.OpenSessionInViewFilter </filter-class> </filter> <filter-mapping> <filter-name>hibernateFilter</filter-name> <url-pattern>*. spring </url-pattern> </filter-mapping> </web-app>
实现Hibernate的Dao接口来使用打开的会话是很容易的。事实上,如果你已经使用了Spring框架来实现你的Hibernate Dao,很可能你不需要改变任何东西。方便的HibernateTemplate公用组件使访问数据库变成小菜一碟,而DAO接口只有通过这个组件才可以访问到数据库。下面是一个示例的DAO:
public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {
public Product getProduct(Integer productId) {
return (Product)getHibernateTemplate().load(Product.class, productId);
}
public Integer saveProduct(Product product) {
return (Integer) getHibernateTemplate().save(product);
}
public void updateProduct(Product product) {
getHibernateTemplate().update(product);
}
}
业务逻辑层中使用延迟加载
即使在视图外面,Spring框架也通过使用AOP 拦截器 HibernateInterceptor来使得延迟加载变得很容易实现。这个Hibernate 拦截器透明地将调用配置在Spring应用程序上下文中的业务对象中方法的请求拦截下来,在调用方法之前打开一个Hibernate会话,然后在方法执行完之后将会话关闭。让我们来看一个简单的例子,假设我们有一个接口BussinessObject:
public interface BusinessObject {
public void doSomethingThatInvolvesDaos();
}
类BusinessObjectImpl实现了BusinessObject接口:
public class BusinessObjectImpl implements BusinessObject {
public void doSomethingThatInvolvesDaos() {
// lots of logic that calls
//DAO classes Which access
//data objects lazily
}
}
通过在Spring应用程序上下文中的一些配置,我们可以让将调用BusinessObject的方法拦截下来,再令它的方法支持延迟加载。看看下面的一个程序片段:
<beans> <bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor"> <property name="sessionFactory"> <ref bean="sessionFactory"/> </property> </bean> <bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl"> <property name="someDAO"><ref bean="someDAO"/></property> </bean> <bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"><ref bean="businessObjectTarget"/></property> <property name="proxyInterfaces"> <value>com.acompany.BusinessObject</value> </property> <property name="interceptorNames"> <list> <value>hibernateInterceptor</value> </list> </property> </bean> </beans>
当businessObject被调用的时候,HibernateInterceptor打开一个Hibernate会话,并将调用请求传递给BusinessObjectImpl对象。当BusinessObjectImpl执行完成后,HibernateInterceptor透明地关闭了会话。应用层的代码不用了解任何持久层逻辑,还是实现了延迟加载。
单元测试中测试延迟加载
最后,我们需要用J-Unit来测试我们的延迟加载程序。我们可以轻易地通过重写TestCase类中的setUp和tearDown方法来实现这个要求。我比较喜欢用这个方便的抽象类作为我所有测试类的基类。
public abstract class MyLazyTestCase extends TestCase {
private SessionFactory sessionFactory;
private Session session;
public void setUp() throws Exception {
super.setUp();
SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
session = SessionFactoryUtils.getSession(sessionFactory, true);
Session s = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
}
protected Object getBean(String beanName) {
//Code to get objects from Spring application context
}
public void tearDown() throws Exception {
super.tearDown();
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
}
}
很多使用WEB的朋友都喜欢利用openSessionView来加载这个问题。不错我也喜欢用,简单方便。但是说几种情况:
1.在quartz中总不能使用openSessionView模式吧
2.在spring后拦截的过程中,如果取拦截对象中的子对象利用openSessionView也不行。
还有很多情况是不能用的,那么怎么解决呢。有人说那就lazy=False吧,你想在单纯的解决一个quartz的时候调整整个WEB工程的加载好像不合适吧。
那怎么办呢,我觉得这个时候就可以考虑利用SQL语句使用这个重新加载一遍这个代理类,这样在想调用的时候才手动加载比较合适和方便吧,但是估计要多几行代码,好好考虑
这样你是拿不到的,因为这时候session已经关闭了,主要由于你的one-to-many中lazy设置为true,如果确实需要延迟加载,可以在session没关闭前,调用Hibernate.initialize(pojo.getChildren());或者pojo.getChildren().siz(),让集合代理类初始化,并加载关联对象集合,这一动作也要在session关闭之前操作。
美文美图
J2EE进阶(八)Hibernate与延迟加载机制探究的更多相关文章
- J2EE进阶(十七)Hibernate中常用的HQL查询方法(getHibernateTemplate())
J2EE进阶(十七)Hibernate中常用的HQL查询方法(getHibernateTemplate()) 当我们使用Hibernate进行数据的CRUD操作时,利用模版进行操作不失为一种方法. ...
- 如何理解Hibernate的延迟加载机制?在实际应用中,延迟加载与Session关闭的矛盾是如何处理的?
延迟加载就是并不是在读取的时候就把数据加载进来,而是等到使用时再加载.Hibernate使用了虚拟代理机制实现延迟加载,我们使用Session的load()方法加载数据或者一对多关联映射在使用延迟加载 ...
- 如何理解Hibernate的延迟加载机制?
延迟加载就是并不是在读取的时候就把数据加载进来,而是等到使用时再加载.Hibernate使用了虚拟代理机制实现延迟加载.返回给用户的并不是实体本身,而是实体对象的代理.代理对象在用户调用getter方 ...
- Hibernate延迟加载机制
延迟加载: 延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作.在Hibernate中提供了对实体对象的延迟加载以及对集合的延迟加载,另 ...
- Hibernate延迟加载机制详解
摘自 http://blog.chinaunix.net/uid-20577907-id-3129234.html 1 延迟加载: 延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是 ...
- J2EE进阶(十九)FileNotFoundException: http://hibernate.org/dtd/hibernate-mapping-3.0.dtd
J2EE进阶(十九)Nested exception: java.io.FileNotFoundException: http://hibernate.org/dtd/hibernate-mappin ...
- J2EE进阶(十八)基于留言板分析SSH工作流程
J2EE进阶(十八)基于留言板分析SSH工作流程 留言板采用SSH(Struts1.2 + Spring3.0 + Hibernate3.0)架构. 工作流程(以用户登录为例): 首先是用 ...
- J2EE进阶(十六)Hibernate 中getHibernateTemplate()方法使用
J2EE进阶(十六)Hibernate 中getHibernateTemplate()方法使用 spring 中获得由spring所配置的hibernate的操作对象,然后利用此对象进行,保存,修 ...
- J2EE进阶(十五)MyEclipse反向工程实现从数据库反向生成实体类之Hibernate方式
J2EE进阶(十五)MyEclipse反向工程实现从数据库反向生成实体类之Hibernate方式 反向工程又称逆向工程. 开发项目涉及到的表太多,一个一个的写JAVA实体类很是费事.MyEcl ...
随机推荐
- Hash算法入门指南(聊点不一样的算法人生)
前言 很多人到现在为止都总是问我算法该怎么学啊,数据结构好难啊怎么的,学习难度被莫名的夸大了,其实不然.对于一个学计算机相关专业的人都知道,数据结构是大学的一门必修课,数据结构与算法是基础,却常常容易 ...
- 使用word
同样这也是生活中常用到的办公软件,word本质是一个排版软件,它与一般的编辑器不同的是,它将整个文本分成了一页一页的,当然这也是方便于打印文档. 使用word还是很容易的,一般来说需要注意以下几方面的 ...
- 3行代码快速实现Spring Boot Oauth2服务
这里的3行代码并不是指真的只需要写3行代码,而是基于我已经写好的一个Spring Boot Oauth2服务.仅仅需要修改3行数据库配置信息,即可得到一个Spring Boot Oauth2服务. 项 ...
- 光电转研发:和计算机没有一点关系的专业怎么去bat类的公司
光电 女 其实编码能力一般般,拿到百度腾讯研发offer. 一来幸运,二来真的想说行动决定了结果.研一没事就出去家教充实自己赚点钱,研二就开始找实习,去了网易,海康威视,百度实习.感觉还是吃了不少苦的 ...
- 一个成功的 Git 分支模型(适用于商业应用开发)
在这篇文章中,我将推广一下大约一年前我介绍过的一些项目(公私皆有)中使用的开发模型,它们的结果都非常成功.有段时间我非常想写出来分享一下,但是我至今才抽出时间来.我不会言及任何项目细节,仅讨论分支策略 ...
- 初识Redis系列之二:安装及简单使用
仅介绍windows下的安装 一:下载地址:https://github.com/MSOpenTech/redis/releases. Redis 支持 32 位和 64 位.这个需要根据你系统平台的 ...
- 【阿里聚安全·安全周刊】Google“手枪”替换 | 伊朗中央银行禁止加密货币
本周七个关键词:Google"手枪"替换丨IOS 漏洞影响工业交换机丨伊朗中央银行禁止加密货币丨黑客针对医疗保健丨付费DDoS攻击丨数据获利的8种方式丨MySQL 8.0 正式版 ...
- linux系统运维面试题简答
1. 简述常用高可用技术 解答: Keepalived:Keepalived是一个保证集群高可用的服务软件,用来防止单点故障,使用VRRP协议实现.在master和backup之间通过mast ...
- 转:rabbitmq——用户管理
原文:http://my.oschina.net/hncscwc/blog/262246?p={{currentPage-1}} 安装最新版本的rabbitmq(3.3.1),并启用managemen ...
- leetcode刷题笔记342 4的幂
题目描述: 给定一个整数 (32位有符整数型),请写出一个函数来检验它是否是4的幂. 示例:当 num = 16 时 ,返回 true . 当 num = 5时,返回 false. 问题进阶:你能不使 ...