Hibernate知识点复习之四

Hibernate知识点复习之四

Hibernate的检索

检索方式分类:对象图导航检索方式,OID检索方式,HQL检索方式,QBC检索方式,SQL检索方式

1 对象图导航检索方式:

根据已加载的对象,导航到他的关联对象,它是利用类与类的关系来检索对象,如要查找一个联系人对应的客户,
就可以由联系人对象自动导航找到联系人所属的客户对象,前提是必须在映射文件中配置多对一的关系,其检索方式如下:
LinkMan linkMan=(LinkMan)session.get(Customer.class,1L); 
Customer customer=linkMan.getCustomer();

2 OID检索方式

概述:OID检索方式主要指用Session的get()和load()方法加载某条记录对应的对象,方式如下:
        Customer customer1 = session.get(Customer.class,3L);
        Customer customer2 = session.load(Customer.class,4L);
底层深入分析get()和load()的区别:

(1)get不支持lazyd(懒加载/延迟加载),load支持lazy
    lazy表示只有在用到的时候才加载数据,
    如:Student student = (Student)session.load(Student.class,1);  //不会发出SQL语句
          student.getName();   //这条语句才会发出SQL语句
     而使用get方法,Student student = (Student)session.get(Student.class,1);  //会发出SQL语句
(2) 采用get加载数据,如果数据库中不存在相应的数据,那么返回null;
    采用load加载数据,如果数据库中不存在相应的数据,那么抛出ObjectNotFoundException
 (3)load方法可返回实体的代理类实例,而get方法永远直接返回实体类。
 (4) load方法可以充分利用内部缓存和二级缓存中的现有数据
     get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。
Session在加载实体对象时,将经过的过程:
    首先,Hibernate中维持了两级缓存。第一级缓存由Session实例维护,其中保持了Session当前所有关联实体的数据,也称为内部缓存。
            而第二级缓存则存在于SessionFactory层次,由当前所有由本SessionFactory构造的Session实例共享。
            出于性能考虑,避免无谓的数据库访问,Session在调用数据库查询功能之前,会先在缓存中进行查询。
            首先在第一级缓存中,通过实体类型和id进行查找,如果第一级缓存查找命中,且数据状态合法,则直接返回。
            之后,Session会在当前“NonExists”记录中进行查找,如果“NonExists”记录中存在同样的查询条件,则返回null。
         “NonExists”记录了当前Session实例在之前所有查询操作中,未能查询到有效数据的查询条件(相当于一个查询黑名单列表)。
            如此一来,如果Session中一个无效的查询条件重复出现,即可迅速作出判断,从而获得最佳的性能表现。
        对于load方法而言
            如果内部缓存中未发现有效数据,则查询第二级缓存,如果第二级缓存命中,则返回。
            如在缓存中未发现有效数据,则发起数据库查询操作(Select SQL),如经过查询未发现对应记录,
            则将此次查询的信息在“NonExists”中加以记录,并返回null。
            根据映射配置和Select SQL得到的ResultSet,创建对应的数据对象。
            将其数据对象纳入当前Session实体管理容器(一级缓存)。
            执行Interceptor.onLoad方法(如果有对应的Interceptor)。
            将数据对象纳入二级缓存。
            如果数据对象实现了LifeCycle接口,则调用数据对象的onLoad方法。
            返回数据对象。

3 HQL检索方式(Hibernate官方推荐的查询语言):

概述:HQL是面对对象的查询语言,它和SQL查询语言相似,但它使用的是类,对象和属性概念,没有表和字段概念。
功能:
         * 在查询语句中设定查询条件
          * 支持动态绑定参数
          * 支持分组查询,允许使用group by和having关键字
         * 提供内置的聚合函数,如sum()和min(),max()
          * 支持子查询,即嵌套查询
         * 支持分页查询
         * 支持投影查询,即仅检索出对象的部分属性
         * 能够调用用户定义的sql函数
完整的hql语句结构:
    select ...from...where...group by ...having... order by ...asc/desc
HQl检索API示例:
    1 hql基本检索
        String hql="from Customer";
        //String hql="select * from Customer";此句Hibernate无法识别*通配符
        //String hql="select c from Customer c;使用select 需要为Customer起别名
        Query query = session.createQuery(hql);
        List<Customer> list = query.list();
    2 排序检索
        String hql="from Customer order by cust_id desc";
        Query query = session.createQuery(hql);
        List<Customer> list = query.list();
    3 条件查询:
        按位置绑定参数
        String hql1="from Customer where cust_id = ?";
        Query query1 = session.createQuery(hql1);
        query1.setParameter(0,9L);
        Customer c1 = (Customer) query1.uniqueResult();
        按名称绑定参数
        String hql2="from Customer where cust_id =:myId";
        Query query2 = session.createQuery(hql2);
        query2.setParameter("myId",8L);
        Customer c2 = (Customer) query2.uniqueResult();
    4 分页检索
        String hql="from Customer";
        Query query = session.createQuery(hql);
        //设置分页
        query.setFirstResult(0);
        query.setMaxResults(5);
        List<Customer> list = query.list();
    5 统计检索:count()总记录,sum()算术总和,avg()算术平均值,min()算术最小值,max()算术最大值
        String hql="select count(*) from Customer";
        String hql3="select sum(cust_id) from Customer";
        String hql4="select avg(cust_id) from Customer";
        String hql5="select max(cust_id) from Customer";
        String hql6="select min(cust_id) from Customer";
    6 投影查询--也就是查询对象的某一个属性
        查询单个属性
        String hql="select cust_name from cn.itheima.domain.Customer"; //所有Customer的cust_name属性
        查询多个属性
        String hql2="select cust_name,cust_id from cn.itheima.domain.Customer";
    7 投影的构造方式查询:
        结果识别为Customer实体的属性
        但前提是:Customer实体中一定要存在 相应的构造方法(空参和实参都要有)
        String hql3="select new Customer(cust_id,cust_name) from cn.itheima.domain.Customer";

4 QBC检索:

    1 概述:QBC(Query By Criteria)它由Criteria接口,Criterion接口,Expression类组成。
        Criteria接口是HibernateAPI的一个查询接口,它由Session进行创建。
        Criterion是Criteria的查询条件,在Criteria中提供add(Criterion criterion)方法来添加查询条件
        而Criterion查询条件的创建是通过Restrictions工具类的静态方法:
                Restrictions.eq                     等于
                Restrictions.allEq              对象是Map,使用key/value进行多个等于比较
                Restrictions.gt                 大于>
                Restrictions.ge                 大于等于>=
                Restrictions.lt                 小于
                Restrictions.le                 小于等于
                Restrictions.between            对应sql的between子句
                Restrictions.like                   对应sql的like子句
                Restrictions.in                 对应sql的in子句
                Restrictions.and                and关系
                Restrictions.or                 or关系
                Restrictions.sqlRestriction  sql限定查询
    2 QBC检索API示例:
        (1)QBC基本查询
                //创建Criteria查询对象
                Criteria criteria = session.createCriteria(Customer.class);//相当于select * from customer
                //获取查询结果
                List list = criteria.list();
        (2)Criteria条件查询
                //创建Criteria查询对象
                Criteria criteria = session.createCriteria(Customer.class);
                //设置查询条件
                //Criterion criterion2 = Restrictions.idEq(2l);
                Criterion criterion = Restrictions.eq("cust_id",2l);
                //把查询条件criterion添加进Criteria对象中
                criteria.add(criterion);
                //获取查询结果
                List list = criteria.list();
        (3)Criteria的分页查询
                创建Criteria查询对象
                Criteria criteria = session.createCriteria(Customer.class);
                设置分页参数
                criteria.setFirstResult(0);
                criteria.setMaxResults(1);
                List list = criteria.list();
        (4)Criteria的排序查询
                获取Criteria查询对象
                Criteria criteria = session.createCriteria(Customer.class);
                排序
                //criteria.addOrder(Order.asc("cust_id")); //升序
                criteria.addOrder(Order.desc("cust_id")); //降序
                List list = criteria.list();
        (5)criteria的统计
            criteria.setProjection(Projections.rowCount());  
            //criteria.setProjection(Projections.max("cust_id")); 
            //criteria.setProjection(Projections.min("cust_id"));
            //criteria.setProjection(Projections.avg("cust_id"));
            //criteria.setProjection(Projections.sum("cust_id"));
            List list = criteria.list();

    3 离线的QBC条件查询DetachedCriteria(SSH整合后经常用)
        概述:DetachedCriteria是一种脱离Session来使用的条件查询对象(传统的Criteria对象必须由Session创建)
                  其不受限于session,易于在不同架构层中封装数据进行传递
        API:
                离线DetachedCriteria的创建
                    DetachedCriteria dc=DetachedCriteria.forClass(Xxx.class);
                离线DetachedCriteria转换为正常Criteria,接收Session
                Criteria criteria =dc.getExecutableCriteria(session);
        示例:
                模拟此处在service/web层
                //创建离线DetachedCriteria
                DetachedCriteria dtCriteria = DetachedCriteria.forClass(Customer.class);
                //进行条件 id查询(和普通的查询方式一样)
                dtCriteria.add(Restrictions.idEq(2l));

                模拟此处在dao层
                //获取session对象
                Session session = HibernateUtils.openSession();
                //开启事务
                Transaction tx = session.beginTransaction();
                //操作-------------------------
                //离线DetachedCriteria传递到  dao层,接收session变成正常的 Criteria查询对象
                Criteria criteria = dtCriteria.getExecutableCriteria(session);
                List list = criteria.list();
                //事务提交,资源释放------------------------
                tx.commit();
                session.close();

5 Hibernate本地SQL检索方式:

    概述:采用HQL或QBC检索方式时,Hiberante底层会生成标准的SQL查询语句,适用于所有数据库平台,它们是跨平台的。
              但有的应用程序需根据底层数据库sql方言来生成一些特殊查询语句,这就需用到Hibernate提供的SQL检索方式。
    本地SQL检索的示例代码:
            SQLQuery sqlQuery=session.createSQLQuery("xxxx");

6 Hibernate的多表查询

(1)原生的SQL多表查询:
    & 连接查询:
        <1>交叉连接(开发一般不使用)
                概述:返回的结果是被连接的两个表中所有数据行的笛卡尔积,如A表有10条数据,B表有8条数据,返回的结果就是10*8。
                语法格式
                        格式一:select * from 表1 cross join 表2;
                        格式二:select * from 表1,表2;
        <2>内连接(又称简单连接或自然连接)
                概述:内连接使用比较运算符对两个表的数据进行比较,并列出与连接条件相匹配的数据行,组合成新的记录。
                语法格式:select  查询字段  from  表1  [inner]  join  表2  on 连接条件 
                内连接可再细分两类
                    隐式内连接(隐式就是看不见inner join关键字,用where关键字代替)
                        select * from 表1,表2   where     表1.关系字段=表2.关系字段 
                    显示内连接(存在inner join关键字)
                        select * from 表1 inner join 表2  on  表1.关系字段=表2.关系字段
        <3>外连接
                概述:返回的结果不仅包含符合查询条件的数据,而且还包含左表(左外连接),右表(右外连接),或两个表(全外连接)中所有数据
                外连接完整格式:
                    select * from  表1   left | right   outer  join  表2  on  表1.关系字段=表2.关系字段 where 条件
                左连接:返回左表中所有记录和右表中符合连接条件的记录
                    select * from  表1  left  outer  join  表2  on  表1.关系字段=表2.关系字段   where 条件
                右连接:返回右表中所有记录和左表中符合连接条件的记录
                    select * from  表1  right  outer  join  表2  on  表1.关系字段=表2.关系字段  where 条件
(2)HQL连接查询:
    & 交叉查询
    & 内连接
        显示内连接
        隐式内连接
        迫切内连接
    & 外连接
        左外连接
        右外连接
        迫切外连接
    &查询语法:
        * 概述:HQL连接查询语法和原生的SQL语法差别不大,区别在于HQL连接查询作用的是对象而不是表
        * SQL和HQL内连接示例比较
            SQL显示内连接:
                select  *  from  cst_customer c  inner join  cst_linkman l on c.cust_id = l.lkm_id
            HQL内连接:
                from Customer c inner join c.linkMans
        * HQL的连接不用写关联字段,不用写具体的on条件,直接写关联属性即可。
        * 迫切内连接(在内连接 inner  join 后添加一个fetch关键字。)
                fetch关键字分析:我们会发现无论是内连接还是迫切内连接生成的sql语句是一样的,fetch关键字不会出现在sql语句中,
                                           因为sql语句没有fetch关键字,fetch只能在hql中使用,生成sql后就消失。
                fetch的作用:
                        普通内连接封装数据时,会将属于客户的数据封装到Customer实体中,会将属于联系人的数据封装到LinkMan实体中
                            所以封装后获得的数据为List<Object[]{Customer,LinkMan}>
                            示例: 
                                    String hql="from Customer c inner join c.linkMans";
                                    //返回的List集合存储着一个数组,存储着客户和联系人两个对象 Object[]{Customer,LinkMan}
                                    List<Object[]> list = session.createQuery(hql).list();
                                    for (Object[] objects : list) {
                                        Customer c= (Customer) objects[0];
                                        LinkMan l=(LinkMan) objects[1];
                                        System.out.println(c);
                                        System.out.println(l);
                                    }
                        迫切内连接封装数据时,会将属于客户的数据封装到Customer实体中,会将属于联系人的数据封装到Customer实体中的联系人集合里
                           所以封装后获得的数据为List<Customer>,但要注意的是,迫切内连接会出现重复的记录,这就需我们用到 distinct关键字进行解决
                           示例:
                                    String hql2="select distinct c from Customer c inner join fetch c.linkMans";
                                    List<Customer> list2 = session.createQuery(hql2).list();
                                    for (Customer customer : list2) {
                                        System.out.println(customer);
                                    }
        * Hibernate的内连接和迫切内连接总结:
                两者主要区别在于封装数据的方式,但查询的结果集是一样的,底层生成的sql也一样

Hibernate的查询优化

概述:在很多CRM案例中要用到查询操作,但Hibernate本身的查询效率不是很好,特别在获取关联对象的方面,我们需要对查询语句进行一些优化

Hibernate的抓取策略

1 抓取策略概述:Hibernate的抓取策略是一种提升Hibernate性能的手段,在Hibernate获取关联对象时,对发送语句进行优化,需使用到延迟加载。
2 延迟加载(lazy load 懒加载)概述:Hibernate使用load方法关联对象默认的加载方式,所谓的延迟加载就是当真正需要数据时,才真正执行数据加载操作。
3 延迟加载分类
    <1> 类级别延迟:
            概述:类级别延迟指的是查询某个对象时,是否采用延迟,通常在<class>标签上配置lazy属性
            分析:& Hibernate查询某个对象时,默认选择类级别延迟(<class lazy="true">),所以使用load方法检索某个对象时,
                            不会马上发送sql语句,真正调用调用该对象时才会发送sql语句
                     & 若不想使用延迟加载,可以直接在映射文件上设置<class lazy="false">,也可以用final修饰持久类,
                          使之无法生成代理类,    就会使延迟加载失效。  
                     & 值得一提的是,类级别的延迟加载我们一般不进行修改,采用默认值 lazy="true"
    <2> 关联级别延迟
            概述:关联级别延迟指的是,查询一个对象的关联对象时是否采用延迟加载,这个通常在<set>或<many-to-one>上配置lazy属性
            分析:
                    & <set>标签上的lazy取值
                        % true:默认值,采用延迟加载
                        % false:检索关联对象的时候,不采用延迟加载
                        % extra:极其懒惰
                    &<many-to-one>标签上的lazy取值
                        % proxy:默认值,是否采用延迟取决于一方上的延迟加载lazy值
                        % false:检索关联对象时,不采用延迟加载
                        % no-proxy:不用研究
    <3>延迟加载总结:
             * 延迟加载:仅仅获得没有使用,不进行查询,使用才进行查询
             * 是否对类进行延迟加载:可以通过在实体类的class元素配置lazy属性来控制
             * lazy配置只作用于 load方法,执行该方法时,不发送任何sql语句,只返回一个对象,使用该对象才执行查询
             * lazy="true"--进行延迟加载:加载时不执行,使用时才查询
             * lazy="false"--不进行延迟加载:加载时立即执行
             * 延迟加载的原理是:
             * 它会把获取到的Customer实体对象变成一个 具有加强功能的超级代理类型对象 Customer_$$
             * (增强的功能就是:使用这个对象才执行查询,若不使用则不执行查询) 
             * 值得注意的是:使用懒加载调用属性时要确保 session还在打开,因为它生成的代理对象是根据关联的 session
             *  查询数据库的,否则会报异常 
             * 结论:建议使用延迟加载,因为它可以把节省不必要浪费的资源,提高效率
3 抓取策略指的是查询某个对象时,如何抓取其关联对象,也可通过配置实现:
    * <set>标签上的fetch取值:
        & select :默认值,发送的是普通的select语句
        & join:发送一条迫切左外连接去查询,值得一提的是,set上设置了fetch=join,lazy就会失效。
        & subselect:发送一条子查询语句查询其关联对象
    * <many-to-one>标签上的fetch有两个取值
        & select :默认值,发送的是普通的select语句
        & join:发送一条迫切左外连接去查询
4 抓取策略fetch和延迟加载lazy配合使用分析:
    <1>单表查询加载:fetch="select"
           延迟加载:lazy="true"
    <2>单表查询加载:fetch="select"
           立即加载:lazy="false"
    <3>单表查询加载:fetch="select"
           极其懒加载:lazy="extra",与懒加载效果基本一致,如果想获得集合size(),则仅用count语句进行查询
    <4>多表查询加载:fetch="join"  一次性查询所有的数据,使懒加载失效
            延迟加载:lazy="true"
    <5>子查询:fetch="subselect"  主要用于重复繁杂查询数据过程中,否则和 select效果一样
            延迟加载:lazy="true"
    <5>子查询:fetch="subselect"  主要用于重复繁杂查询数据过程中,否则和 select效果一样
             立即加载:lazy="false"
5 <set>集合上的fetch和lazy特点总结
    (1)fetch:控制的是 查询其关联对象时采用的sql语句格式
        * select:默认值,发送一条select语句查询其关联对象
        * join:发送一条迫切左外连接查询其关联对象
        * subselect:发送一条子查询查询其关联对象
    (2)lazy:控制的是 查询其关联对象时采用的延迟加载策略
        * true:延迟加载
        * false:立即加载
        * extra:极其懒惰
6 批量抓取(同时查询多个对象的关联对象)
    (1)实现批量查询一方关联的多方:
        <set>标签内配置 batch-size属性,如:
        <!--批量抓取 查询一次,抓取集合数量为3
            抓取客户的集合时,一次抓取多个客户联系人的集合
        -->
        <set name="linkMans" batch-size="3" >
            <key column="lkm_cust_id" ></key>
            <one-to-many class="LinkMan"/>
        </set>
    (2)实现批量查询多方关联的一方:
        在多方的<class>标签中配置  batch-size即可
        注意,并不是在<many-to-one>标签内配置

猜你喜欢

转载自blog.51cto.com/14008076/2303718