(十六)Hibernate中的延迟加载
一、什么是延迟加载
为了节省Hibernate加载对象的性能节销,在Hibernate中真正需要用到这个对象时,才会发出
SQL语句来抓取这个对象。这一个过程称为延迟加载。
二、延迟加载的分类
A:实体对象的延迟加载
B:一对多|多对多的延迟加载
C:多对一|一对一的延迟加载
D:属性的延迟加载
A:实体对象的延迟加载:使用session.get()和session.load()获取对象的区别就是是否开启延迟加载。
Hibernate只加载实体对象的ID,需要其他属性,才真正的发出SQL来加载这个对象。
Load:采用延迟加载 加载到的是一个代理对象
Get:没有采用延迟加载 加载到的是一个实体对象。
- 案例:
User user=(User)session.load(clazz, id);//直接返回的是代理对象
System.out.println(user.getId());//没有发送sql语句到数据库加载
user.getName();//创建真实的User实例,并发送sql语句到数据库中
- 注意:1.不能判断User=null;代理对象不可能为空
2.代理对象的限制:和代理关联的session对象,如果session关闭后访问代理则抛异常。session关闭之前访问数据库
B:一对多|多对多的延迟加载
fetch = FetchType.Lazy:表示开启延迟加载。读取班级时,不会发出读取学生的SQL语句。等真正使用学生数据时,才会发出一条SQL语句读取学生
fetch = FetchType.EAGER:取消延迟加裁。读取班级会左关联读取学生。
@OneToMany(cascade = { CascadeType.REMOVE },fetch=FetchType.EAGER)
@JoinColumn(name = "classes_id")
@OrderBy(value = " studentID desc")
public List<StudentBean> getStuList() {
return stuList;
}
C : 多对一|一对一的延迟加裁
默认是取消延迟加载的。
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "group_id")
private GroupBean groupBean;
- 延迟加载带来的问题: session关闭之后,再访问代理对象(延迟加载获取的是代理对象)会抛出“no session”异常。
package action; import java.util.Set; import javassist.compiler.ast.IntConst; import org.hibernate.Session;
import org.hibernate.Transaction; import bean.ClassBean;
import bean.StudentBean;
import util.HibernateSessionUtil; public class Test {
public static void main(String[] args) { ClassBean cla=Test.load(); //当Test.load()执行完毕之后,session就被关闭,这时候再访问代理对象则会抛出异常。 使用session。get就不会出现这个问题
System.out.println(cla.getClassName());
} private static ClassBean load() { ClassBean cla = null;
Session session = null;
Transaction tran = null; try {
session = HibernateSessionUtil.getSession();
tran = session.beginTransaction(); cla = (ClassBean) session.load(ClassBean.class, new Integer(2)); //使用延迟加载,获得的是代理对象 tran.commit();
return cla;
} catch (Exception e) {
e.printStackTrace();
tran.rollback();
} finally { HibernateSessionUtil.closeSession(); //关闭session
} return null;
}
}
- 橙色字体处代码会出现“no session”异常。
解决延迟加载带来的问题:
1. 在后台,把前台要显示的数据准备好。(适用于非WEB程序和WEB程序)
2. 使用延迟加载,又要把Session关掉。而且是前台展现数据的时候,才发给SQL语句。(仅限于WEB程序) 原理:将Session的关闭延迟到页面加载完成之后,才关闭。
3. 在2的的基础上面,将在页面中手工关闭的Session代码,改为自动调用关闭。 过滤器来实现。
1. 使用第一种方法解决延迟加载带来的问题(在后台,把前台要显示的数据准备好)
package action; import java.util.HashMap;
import java.util.Map;
import java.util.Set; import javassist.compiler.ast.IntConst; import org.hibernate.Session;
import org.hibernate.Transaction; import bean.ClassBean;
import bean.StudentBean;
import util.HibernateSessionUtil; public class Test {
public static void main(String[] args) {
Map<String, Object> dataMap = Test.load(); // Test.load()方法不再直接返回一个ClassBean对象然后再由这个对象得到StudentBean对象,而是
// Test.load()方法里直接把ClassBean和StudentBean对象直接返回 ClassBean classBean = (ClassBean) dataMap.get("classBean");
Set<StudentBean> stuSet=(Set<StudentBean>)dataMap.get("stuSet");
System.out.println(stuSet.size());
} private static Map<String, Object> load() {
Map<String, Object> dataMap = new HashMap<String, Object>();
Session session = null;
Transaction tran = null; try {
session = HibernateSessionUtil.getSession();
tran = session.beginTransaction(); ClassBean classBean = (ClassBean) session.get(ClassBean.class,
new Integer(1));
Set<StudentBean> stuSet = classBean.getStuSet();
dataMap.put("classBean", classBean);
stuSet.size(); //这行不能省略,因为classBean.getStuSet();并不会发出sql语句。
dataMap.put("stuSet", stuSet); tran.commit(); } catch (Exception e) {
e.printStackTrace();
tran.rollback();
} finally { HibernateSessionUtil.closeSession(); // 关闭session
} return dataMap;
}
}
2.使用第二种方法解决延迟加载带来的问题(是前台展现数据的时候,才发给SQL语句。(仅限于WEB程序))
- index.jsp
<body>
<a href="<%=path%>/servlet/session_1">1:解决延迟加载,将Session的关闭延迟到jsp页面中</a>
</body>
- SessionServlet .java
package servlet;
import java.io.IOException;
import java.io.PrintWriter; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.hibernate.Session; import bean.ClassBean;
import util.HibernateSessionUtil; public class SessionServlet extends HttpServlet { public SessionServlet() {
super();
} public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { this.doPost(request, response);
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { request.setCharacterEncoding("UTF-8");
response.setContentType("html;charset=UTF-8"); Session session = null;
ClassBean classBean = null;
try { session = HibernateSessionUtil.getSession();
classBean = (ClassBean) session.load(ClassBean.class,
new Integer(1)); } catch (Exception e) {
e.printStackTrace();
} finally {
//这里不能关闭session,在view1,jsp页面关闭seession
} request.setAttribute("classBean", classBean); request.getRequestDispatcher("/view1.jsp").forward(request, response); } }
- view1.jsp
<body>
<pre>
<h2>班级信息:</h2>
班级id:${requestScope.classBean.classId}
班级名称:${requestScope.classBean.className} <h2>学生信息信息:</h2>
<c:forEach var="student" items="${requestScope.classBean.stuSet}">
学生id:${student.stuId}
学生名:${student.stuName}
班级id:${student.classId}
</c:forEach>
</pre>
<%
HibernateSessionUtil.closeSession(); //在这里关闭session,确保页面取到所需要的数据后再关闭session。
%>
</body>
结果:
3. 案例三(在2的的基础上面,将在页面中手工关闭的Session代码,改为自动调用关闭。 过滤器来实现。)
- index.jsp
<body>
<a href="<%=path%>/servlet/session_1">1:在过滤器中统一关闭session</a>
</body>
- SessionServlet.java
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { request.setCharacterEncoding("UTF-8");
response.setContentType("html;charset=UTF-8"); Session session = null;
ClassBean classBean = null;
try { session = HibernateSessionUtil.getSession();
classBean = (ClassBean) session.load(ClassBean.class,
new Integer(1)); } catch (Exception e) {
e.printStackTrace();
} finally {
//这里不能关闭session,在view1,在过滤器中关闭seession
} request.setAttribute("classBean", classBean); request.getRequestDispatcher("/view1.jsp").forward(request, response); }
- HibernateFilter.java
package filter; import java.io.IOException; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder; public class HibernateFilter implements Filter { private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
private static org.hibernate.SessionFactory sessionFactory; private static Configuration configuration = new Configuration();
private static ServiceRegistry serviceRegistry; @Override
public void init(FilterConfig arg0) throws ServletException {
try {
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder().applySettings(
configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
System.err.println("%%%% Error Creating SessionFactory %%%%");
e.printStackTrace();
}
} @Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException { try {
System.out.println("hello");
chain.doFilter(req, res);
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateFilter.closeSession(); // 在过滤器中统一关闭session
} } @Override
public void destroy() { } public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get(); if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession()
: null;
threadLocal.set(session);
} return session;
} public static void rebuildSessionFactory() {
try {
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder().applySettings(
configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
System.err.println("%%%% Error Creating SessionFactory %%%%");
e.printStackTrace();
}
} public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null); if (session != null) {
session.close();
}
} }
- view1.jsp
<body>
<pre>
<h2>班级信息:</h2>
班级id:${requestScope.classBean.classId}
班级名称:${requestScope.classBean.className} <h2>学生信息信息:</h2>
<c:forEach var="student" items="${requestScope.classBean.stuSet}">
学生id:${student.stuId}
学生名:${student.stuName}
班级id:${student.classId}
</c:forEach>
</pre> </body>
结果与上例差不多。
总结:一般使用第二种解决方式来解决延迟加载带来的问题。
D. 属性的延迟加载
- 大字段的属性上面(Oracle中的Clob和Blog,SQLServer中的TExt和Image2种字段),String,int属性没有必要延迟加载。
1:设定延迟加载的注解
// Text映射为string类型
@Lob
@Basic(fetch = FetchType.LAZY)
private String content;
// image映射为字节数组。
@Lob
@Basic(fetch = FetchType.LAZY)
private byte[] filedata;
2:要对对象实现类增强机制。
使用Ant来完成。
- build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="Hibernate_Project_7" default="instrument" basedir=".">
<property name="lib.dir" value="./WebRoot/WEB-INF/lib" />
<property name="classes.dir" value="./WebRoot/WEB-INF/classes" /> <path id="lib.class.path">
<fileset dir="${lib.dir}">
<include name="**/*.jar" />
</fileset>
</path>
<target name="one"></target>
<target name="two"></target>
<target name="instrument">
<taskdef name="instrument"
classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
<classpath path="${classes.dir}" />
<classpath refid="lib.class.path" />
</taskdef>
<instrument verbose="true">
<fileset dir="${classes.dir}/com/bean">
<include name="LobBean.class" /> <!-- 每次修改LobBean的代码后都需要重新运行ant,否则这个ant会失效 -->
</fileset>
</instrument>
</target>
</project>
- 案例
BlobBEAN.java
package bean; import java.io.Serializable; import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table; @Entity
@Table(name = "t_blob")
public class BlobBEAN implements Serializable {
@Id
@Column(name = "blodid")
private Integer blobId; private String name; // String类型映射为文本大字段 @Basic(fetch=FetchType.LAZY)
@Lob
private String content;
// 视频/图片等大字段映射为字节数组 @Basic(fetch=FetchType.LAZY)
@Lob
private byte[] image; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public BlobBEAN(Integer blobId, String name, String content, byte[] image) {
super();
this.blobId = blobId;
this.name = name;
this.content = content;
this.image = image;
} public BlobBEAN() {
super();
// TODO Auto-generated constructor stub
} public Integer getBlobId() {
return blobId;
} public void setBlobId(Integer blobId) {
this.blobId = blobId;
} public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
} public byte[] getImage() {
return image;
} public void setImage(byte[] image) {
this.image = image;
} }
- build.xml
<?xml version="1.0" encoding="UTF-8"?> <project name="hibernate_lazy" default="instrument" basedir="."> <!--default指默认执行的arget -->
<property name="lib.dir" value="./WebRoot/WEB-INF/lib" /> <!--设置lib文件夹的路径 -->
<property name="classes.dir" value="./WebRoot/WEB-INF/classes" /> <!--设置classes文件夹的路径 --> <path id="lib.class.path">
<fileset dir="${lib.dir}">
<include name="**/*.jar" /> <!-- 引用${lib.dir}路径中所有的jar包-->
</fileset>
</path>
<target name="instrument">
<taskdef name="instrument"
classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
<classpath path="${classes.dir}" />
<classpath refid="lib.class.path" />
</taskdef>
<instrument verbose="true">
<fileset dir="${classes.dir}/bean">
<include name="BlobBEAN.class" /> <!-- 每次修改BlobBEAN的代码后都需要重新运行ant,否则这个ant会失效 -->
</fileset>
</instrument>
</target>
</project>
- Test.java
package action; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream; import org.apache.commons.io.IOUtils;
import org.hibernate.Session;
import org.hibernate.Transaction; import bean.BlobBEAN;
import util.HibernateSessionUtil; public class Test {
public static void main(String[] args) {
// Test.save();
Test.load();
} private static void save() {
Session session = null;
Transaction tran = null; try {
session = HibernateSessionUtil.getSession();
tran = session.beginTransaction(); BlobBEAN blobBean = new BlobBEAN();
blobBean.setBlobId(1); // Text类型
StringBuffer str = new StringBuffer();
for (int i = 0; i < 10000; i++) {
str.append("abcabc");
}
blobBean.setContent(str.toString()); // Image类型
String path = "F:\\123.jpg";
InputStream inputStream = new FileInputStream(new File(path));
byte[] imaBytes = IOUtils.toByteArray(inputStream);
blobBean.setImage(imaBytes); session.save(blobBean); tran.commit();
} catch (Exception e) {
e.printStackTrace();
tran.rollback();
} finally {
HibernateSessionUtil.closeSession();
} } private static void load() { Session session = null;
Transaction tran = null; try {
session = HibernateSessionUtil.getSession();
tran = session.beginTransaction(); BlobBEAN blob=(BlobBEAN)session.get(BlobBEAN.class, new Integer(1));
System.out.println(blob.getName()); //属性的延迟加载,加载任意一个大字段时,会加载所有的属性延迟字段。
String content=blob.getContent();
OutputStream out=new FileOutputStream(new File("F:\\123.txt"));
IOUtils.write(content, out); out.flush();
out.close(); tran.commit();
} catch (Exception e) {
e.printStackTrace();
tran.rollback();
} finally {
HibernateSessionUtil.closeSession();
} } }
(十六)Hibernate中的延迟加载的更多相关文章
- J2EE进阶(十六)Hibernate 中getHibernateTemplate()方法使用
J2EE进阶(十六)Hibernate 中getHibernateTemplate()方法使用 spring 中获得由spring所配置的hibernate的操作对象,然后利用此对象进行,保存,修 ...
- Hibernate中的延迟加载及fetch
Hibernate中的延迟加载 1.类级别的查询策略: lazy : true(默认值) false(立即加载) 2.多对一关联的查询策略: lazy: proxy(默认值) no-proxy ...
- 【Hibernate】浅析hibernate中的延迟加载
1 简介 在使用一些查询方法时,方法执行了,但是并没有立刻发送SQL语句查询数据库.而是在访问对象的getXxx方法时候才触发SQL执行加载对象数据.这种机制就称为延迟加载. 2 优点 延迟加载主要是 ...
- 《C++游戏开发》十六 游戏中的寻路算法(二):迷宫&A*算法基础
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/10289253 作者:七十一雾央 新浪微博:http: ...
- WPF入门教程系列十六——WPF中的数据绑定(二)
三.绑定模式 通过上一文章中的示例,学习了简单的绑定方式.在这里的示例,要学习一下绑定的模式,和模式的使用效果. 首先,我们来做一个简单示例,这个示例是根据ListBox中的选中项,去改变TextBl ...
- (十六)WebGIS中偏移补偿量引发的问题之探讨
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 在上一章里讲解地图平移功能的实现时,我在最后提出了两个问题: ...
- JavaWeb学习 (十六)————JSP中的九个内置对象
一.JSP运行原理 每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理.JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet ...
- Java学习笔记十六:Java中的构造方法
Java中的构造方法 1.使用new+构造方法 创建一个新的对象: 2.构造方法是定义在Java类中的一个用来初始化对象的方法: 3.构造方法与类同名且没有返回值: 4.语法格式: public 构造 ...
- Android之旅十六 android中各种资源的使用
android中各种资源的使用: 在android开发中,各种资源的合理使用应该在各自的xml中进行定义,以便反复使用; 字符串资源:strings.xml,xml中引用:@string/XXX,ja ...
随机推荐
- 阿里云OSS的 存储包、下行流量包、回流流量包 三者有啥关系
阿里云OSS的 存储包.下行流量包.回流流量包 三者有啥关系 一.总结 一句话总结: 你把文件放 oss,会占用存储空间,存储包覆盖这部分费用 你访问存储在 oss 里面的文件,会产生下行流量,就是从 ...
- world: 对比两个文档
1. 2. 3. 4.
- keras Model 1 入门篇
1 入门 2 多个输入和输出 3 共享层 最近在学习keras,它有一些实现好的特征提取的模型:resNet.vgg.而且是带权重的.用来做特诊提取比较方便 首先要知道keras有两种定义模型的方式: ...
- python小白之np功能快速查
np一些用法 np.a np.array([1,2,3],dtype=int) #建立一个一维数组, np.array([[1,2,3],[2,3,4]]) #建立一个二维数组. np.arang ...
- Linux交换空间(swap space)的那些优缺点
下面的所有例子都在ubuntu-server-x86_64 16.04下执行通过 什么是swap? swap space是磁盘上的一块区域,可以是一个分区,也可以是一个文件,或者是他们的组合.简单点说 ...
- js 点击列表li,获得当前li的id
html <ul id="demo"> <li id="li-1">li1</li> <li id="li- ...
- sql数据库为null时候ASP语句判断问题
我有一个表test1,有字段num,字段num有null值,也有空值,也有其他值,我要用asp语句判断我查询出来的num的值是否为null值.应该怎么写 严谨一点,要有两层判断: If IsNull( ...
- 二进制包安装Mysql
(1).准备工作 前往mysql官网下载二进制安装包,https://dev.mysql.com/downloads/mysql/5.7.html#downloads(注意:选择操作系统时选Linux ...
- tcpdump抓包代码
tcpdump - tcp[:]=:]=0x4854 or tcp 抓出来的包可以导入wireshark分析 以上代码曾经在ios越狱机器上使用,用于抓包,具体也记不起来了 导入wireshark效果
- WPF窗体应用程序开发
1.Window:登录窗口.主窗体.消息框 2.UserControl:业务界面.消息框(如果使用UC来实现,则需要做特殊的处理,比如中断功能如何处理?)(显示一个UC,必须将其添加到容器中,所以&l ...