zoukankan      html  css  js  c++  java
  • (十六)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();
            }
    
        }
    
    }


          

  • 相关阅读:
    [java学习]java聊天室通信原理
    竖变横表存储过程(万能型)
    到底是什么(反射,泛型,委托,泛型)
    删除表里重复记录两种方法
    三个SQL视图查出所有SQL Server数据库字典
    三种分页语句
    DBHelper
    SQL全局变量
    今天比较STRING和INT,很奇怪
    表之间数据交换与翻页存储过程
  • 原文地址:https://www.cnblogs.com/shyroke/p/6905609.html
Copyright © 2011-2022 走看看