一、什么是延迟加载

为了节省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中的延迟加载的更多相关文章

  1. J2EE进阶(十六)Hibernate 中getHibernateTemplate()方法使用

    J2EE进阶(十六)Hibernate 中getHibernateTemplate()方法使用   spring 中获得由spring所配置的hibernate的操作对象,然后利用此对象进行,保存,修 ...

  2. Hibernate中的延迟加载及fetch

    Hibernate中的延迟加载 1.类级别的查询策略: lazy  :  true(默认值) false(立即加载) 2.多对一关联的查询策略: lazy:  proxy(默认值) no-proxy ...

  3. 【Hibernate】浅析hibernate中的延迟加载

    1 简介 在使用一些查询方法时,方法执行了,但是并没有立刻发送SQL语句查询数据库.而是在访问对象的getXxx方法时候才触发SQL执行加载对象数据.这种机制就称为延迟加载. 2 优点 延迟加载主要是 ...

  4. 《C++游戏开发》十六 游戏中的寻路算法(二):迷宫&A*算法基础

    本系列文章由七十一雾央编写,转载请注明出处.  http://blog.csdn.net/u011371356/article/details/10289253 作者:七十一雾央 新浪微博:http: ...

  5. WPF入门教程系列十六——WPF中的数据绑定(二)

    三.绑定模式 通过上一文章中的示例,学习了简单的绑定方式.在这里的示例,要学习一下绑定的模式,和模式的使用效果. 首先,我们来做一个简单示例,这个示例是根据ListBox中的选中项,去改变TextBl ...

  6. (十六)WebGIS中偏移补偿量引发的问题之探讨

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 在上一章里讲解地图平移功能的实现时,我在最后提出了两个问题: ...

  7. JavaWeb学习 (十六)————JSP中的九个内置对象

    一.JSP运行原理 每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理.JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet ...

  8. Java学习笔记十六:Java中的构造方法

    Java中的构造方法 1.使用new+构造方法 创建一个新的对象: 2.构造方法是定义在Java类中的一个用来初始化对象的方法: 3.构造方法与类同名且没有返回值: 4.语法格式: public 构造 ...

  9. Android之旅十六 android中各种资源的使用

    android中各种资源的使用: 在android开发中,各种资源的合理使用应该在各自的xml中进行定义,以便反复使用; 字符串资源:strings.xml,xml中引用:@string/XXX,ja ...

随机推荐

  1. Link static data in sql source control

    You can link data that doesn't change very often to SQL Source Control. This lets you commit data ch ...

  2. Rabbitmq Plugin configuration unchanged. 问题完全解决方案

    当执行:rabbitmq-plugins.bat enable rabbitmq_management 命令时候出现 错误如下: Plugin configuration unchanged. App ...

  3. java从包package中获取所有的Class

      1.从包package中获取所有的Class方法: /** * 从包package中获取所有的Class * @param pack * @return */ public static List ...

  4. web手工项目01-系统组织框架-测试流程-需求评审-测试计划与方案

    回顾 SVN(定义,作用,使用操作) 软件缺陷(定义,表现形式,原因和根源,基本内容,跟踪流程) JIRA(基本介绍,使用者,工作流,问题,使用) 学习目标 掌握WAMP的环境搭建 掌握熟悉项目的步骤 ...

  5. Delphi分割字符串函数Split源码

    function TStringHelper.Split(const Separator: array of string; Count: Integer; Options: TStringSplit ...

  6. (十)Centos之文件搜索命令find

    1.1 find [搜索范围] [搜索条件](搜索文件) find是在系统当中搜索符合条件的文件名. 如果需要匹配,使用通配符匹配,通配符是完全匹配. * 匹配任意内容 ?匹配任意一个字符 []匹配任 ...

  7. C# 后台POST和GET 获取数据

    C# 后台POST和GET 获取数据 , data.Length);     newStream.Close();     HttpWebResponse myResponse = (HttpWebR ...

  8. 最新javascript自动按比例显示图片,按比例压缩图片显示

    最新javascript自动按比例显示图片,按比例压缩图片显示 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//E ...

  9. python多进程——fork()

    简介    程序每次执行时,操作系统都会创建一个新进程来运行程序指令.进程中可调用os.fork,要求操作系统新建一个子进程.[Windowsc系统中,os模块没有os.fork函数]. 每个进程都有 ...

  10. idea自动下载依赖的源代码等信息

    mvn dependency:sources -DdownloadSources=true -DdownloadJavadocs=true