(十六)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 ...
随机推荐
- Android:修改连接到AP端显示的设备名
一.Android系统代码中实现设备名分配 1. \frameworks\base\services\core\java\com\android\server\ConnectivityService. ...
- Quartz调度系统入门和调度高可用实现方案
** 版本:2.2.1 ** Hello world: https://www.jianshu.com/p/810400e6a274
- 36 Flutter仿京东商城项目 用户登录 退出登录 事件广播更新状态
Login.dart import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/material.da ...
- EasyUI动态展示用户信息
业务需求:用户登录后展示用户名.用户对应的角色.EasyUI只不过是一个前端的框架,封装了一些组件和样式,你可以用jsp直接调后台获取到用户信息展示,但我这里想用html页面,用目前流行的说法:前后端 ...
- webpack——Modules && Hot Module Replacement
blog:JavaScript Module Systems Showdown: CommonJS vs AMD vs ES2015 官网链接: Modules 官网链接:Hot Module Rep ...
- Java不写文件,LOAD DATA LOCAL INFILE大批量导入数据到MySQL的实现(转)
MySQL使用load data local infile 从文件中导入数据比insert语句要快,MySQL文档上说要快20倍左右.但是这个方法有个缺点,就是导入数据之前,必须要有文件,也就是说从文 ...
- Python扫描器-HTTP协议
1.HTTP协议 1.1.HTTP协议简介 #1.HTTP协议,全称Hyper Text Transfer Protocol(超文本传输协议) HTTP协议是用于从(WWW:World Wide We ...
- Docker 持久存储介绍(十三)
目录 一.Docker 数据存储 二.Bind mount 1.详细介绍 2.如何使用 -v or --volume 语法 --mount 语法 两者区别 3.使用场景 4.使用案例 存在目录 bin ...
- [转载]DriverStore文件夹特别大,能删除吗?
[转载]DriverStore文件夹特别大,能删除吗? 转自博客园https://www.cnblogs.com/lovebing/p/6951833.html 这篇文章,清理完C盘多了20G!不要太 ...
- PLSQL设置文件夹排序并设置颜色