Day41-Hibernate04 --检索方式及增强,离线条件查询,hql多表查询,抓取策略

Hibernate的检索方式:

*      对象图导航检索方式 -- 对象导航查询
*      OID检索方式 -- 用session的get()/load()方法,就是利用id查询
*      HQL检索方式 -- 面向对象的查询语言
*      QBC检索方式
*      SQL检索方式

最优选择QBC(Query By Criteria)查询,特别是离线查询对象很好用;最后选择SQLQuery;


HQL检索方式:

命名查询:
好处:不用特别记住参数是第几个,直接用命名的方式代替;对于参数很多的情况很好用

      @Test
      public void test011() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Query query = session.createQuery("from Employee  where ename = :ename");
           query.setString("ename", "市场1号");
           List list = query.list();
           for (Employee employee : list) {
                 System.out.println(employee);
           }
           // 释放资源
           transaction.commit();
           session.close();
      }

      @Test
      public void test011() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Query query = session.createQuery("from Employee where ename like :ename and eid >:eid");
           query.setString("ename", "%市场%");
           query.setInteger("eid", 5);
           List list = query.list();
           for (Employee employee : list) {
                 System.out.println(employee);
           }
           // 释放资源
           transaction.commit();
           session.close();
      }

投影检索:
其实就是根据不同的字段来查询,如果查询的是多个字段,就会将每条查询结果封装成为一个数组。

      @Test//投影查询
      public void test013() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Query query = session.createQuery("select eid,ename from Employee ");
           List list = query.list();
           for (Object[] object : list) {
                 System.out.println(Arrays.toString(object));
           }
           // 释放资源
           transaction.commit();
           session.close();
      }

投影构造查询:
这种方法需要创建对应参数的构造函数,同时也不要忘记了保留无参数的构造函数,这样只封装了需要的参数到持久化类的对象中去。

      @Test//投影构造查询
      public void test014() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();
           Query query = session.createQuery("select new Employee(eid,ename) from Employee ");
           List list = query.list();
           for (Employee employee : list) {
                 System.out.println(employee);
           }
           // 释放资源
           transaction.commit();
           session.close();
      }

QBC检索方式增强:Query By Criteria

QBC是一种完全面向对象的查询语言,所有使用的方法类都是对象

1)使用Order类进行排序查询
使用order类,通过其order的静态方法产生Order对象(排序对象)
2)api

*      static Order desc(String propertyName)Order类的静态方法,返回Order的实例对象,按照propertyName(持久化类的属性名)进行降序排列。
*      static Order asc(String propertyName)
*      criteria.addOrder(Order order),Criteria对象,增加排序对象
      @Test//Order类,按照单个字段进行排序
      public void test015() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Criteria criteria = session.createCriteria(Employee.class);
           Order desc = Order.desc("eid");
           criteria.addOrder(desc);

           List list = criteria.list();
           for (Object object : list) {
                 System.out.println(object);
           }

           // 释放资源
           transaction.commit();
           session.close();
      }


      @Test//Order类,按照多个字段进行排序
      public void test016() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Criteria criteria = session.createCriteria(Employee.class);
           Order desc = Order.desc("eid");
           Order asc = Order.asc("department");
           //先加入哪一个排序条件就按照哪一个排序条件进行排序,如果相等的情况才会按照第二个排序条件进行排序
           criteria.addOrder(desc);
           criteria.addOrder(asc);

           List list = criteria.list();
           for (Object object : list) {
                 System.out.println(object);
           }

           // 释放资源
           transaction.commit();
           session.close();
      }

2)Projection类
Projection可以完成分组统计 、 获取最大值 、 获取最小值 、 获取记录条数
细节:分组和统计、以及得出最大最小值、符合条件的记录数都用Projection

使用Projection,Projections和ProjectList类

*      Projection:对象,分组统计对象,对于每个分组或者统计条件,创建一个Projection对象
*      Projections:工具类,用于获得Projection实例对象
*      ProjectionList:分组统计对象的集合,用于保存多个分组的统计对象

api:
a) Projections工具类的静态方法,返回值是一个Projection实例对象
Projections.groupProperty(String propertyName)

 b)Projections.max(String propertyName)  //可以用于数据表中某个字段的最大最小值
      Projections.avg(String propertyName)
      Projections.rouCount(String propertyName)
      Projections.count(String propertyName)
      Projections.min(String propertyName)
      Projections.countDistinct(String propertyName)

 c)criteria.setProjection(Projection projection):
      --Criteria对象,设置分组统计对象。

 d) static ProjectionList projectionList():

–获取projection集合对象,因Criteria对象,只能保存一个Projection对象,在进行分组统计的联合使用时,必定存在二个及以上的Projection对象,所以,需要ProjectionList来保存。

案例:

     @Test//查询id最大的员工
      public void test017() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Criteria criteria = session.createCriteria(Employee.class);

           Projection projection = Projections.max("eid");
           criteria.setProjection(projection);

           Object uniqueResult = criteria.uniqueResult();
           System.out.println(uniqueResult);

           // 释放资源
           transaction.commit();
           session.close();
      }

      @Test//查询记录条数
      public void test017() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Criteria criteria = session.createCriteria(Employee.class);

           Projection projection = Projections.count("eid");
           criteria.setProjection(projection);

           Object uniqueResult = criteria.uniqueResult();
           System.out.println(uniqueResult);

           // 释放资源
           transaction.commit();
           session.close();
      }

      @Test//按性别分类和按照分类进行查询各类的记录条数,就是分组统计:先进行分组,然后进行统计
      public void test018() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Criteria criteria = session.createCriteria(Employee.class);
           Projection projection1 = Projections.groupProperty("sex");
           Projection projection2 = Projections.count("id");
           ProjectionList projectionList = Projections.projectionList();
           //为什么用ProjectionList,因为setProjection()方法底层只能够加入一个条件
           projectionList.add(projection1);
           projectionList.add(projection2);
           criteria.setProjection(projectionList);

           List list = criteria.list();
           for (Object[] object : list) {
                 System.out.println(Arrays.toString(object));
           }

           // 释放资源
           transaction.commit();
           session.close();
      }

离线条件查询 DetachedCriteria – QBC的皇冠

  1. DetachedCriteria的定义

    • 翻译成离线条件查询,可以脱离Session使用,即无需依靠Session就可以创建。
    • 普通Criteria对象,必须有Session对象创建,即先有Session对象,后有Criteria对象。因为session中才有这数据库的连接对象,才能够操作数据库
  2. 常见API介绍
    a. static DetachedCriteria forClass(Class clazz): // DetechedCriteria的静态方法,用于创建DetechedCriteria实例对象,clazz指需要查询的持久化类的字节码对象。

    b. 增加查询条件,排序,分组统计,和普通的Criteria完全一样。

    c. Criteria getExecutableCriteria(Session session): //成员方法,通过session对象,获得普通的Criteria对象,完成查询操作。


      @Test//离线条件查询
      public void test020() {
           //创建离线对象
           DetachedCriteria dc = DetachedCriteria.forClass(Employee.class);
           //设定查询条件
           dc.add(Restrictions.gt("eid", 5));

           //获取session
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           //离线查询对象通过session,可以获得可以使用的criteria,相当于复活了
           Criteria criteria = dc.getExecutableCriteria(session);
           List list = criteria.list();
           for (Object object : list) {
                 System.out.println(object);
           }
           transaction.commit();
           session.close();
      }

离线条件查询的价值:
例如京东的查询筛选,筛选条件十分多,而且可能是多选就可以采取离线条件查询,先把条件一个一个的加入到离线对象中去,然后复活对象进行查询。
Day41-Hibernate04 --检索方式及增强,离线条件查询,hql多表查询,抓取策略_第1张图片
Day41-Hibernate04 --检索方式及增强,离线条件查询,hql多表查询,抓取策略_第2张图片


HQL的多表查询(了解)
多表查询只能够使用HQL语言查询

      @Test
      public void test01() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Query query = session.createQuery("from Employee e,Department d where e.department = d.did ");
           //注意,在HQL语言中,判定两个字段是不是相等用的就是其id判断的
           List list = query.list();
           //System.out.println(list.size());
           for (Object[] object : list) {
                 System.out.println(Arrays.toString(object));
           }

           // 释放资源
           transaction.commit();
           session.close();
      }

      @Test //显式内连接
      public void test02() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Query query = session.createQuery("from Employee e inner join Department d on e.department = d.did ");
           List list = query.list();
           //System.out.println(list.size());
           for (Object[] object : list) {
                 System.out.println(Arrays.toString(object));
           }

           // 释放资源
           transaction.commit();
           session.close();
      }

迫切内连接:

迫切内联接:查询到的结果,封装成一个对象,其他的数据,封装到这个对象的关联对象。
使用迫切内连接的时候一定要加上去重的条件

迫切内联接和普通内联接的区别:
a. fetch:表示:迫切
b. 普通内联接:查询到的结果,会封装成2个对象。
c. 迫切内联接:查询到的结果,封装成一个对象,其他的数据,封装到这个对象的关联对象。
* 因为数据存在重复,需要去重:distinct
* 标准写法:select distinct x from Department x inner join fetch x.employees

      @Test //迫切内连接
      public void test03() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Query query = session.createQuery("select distinct d from Department d inner join fetch d.employees");
           List list = query.list();
           for (Department object : list) {
                 System.out.println(object);
           }

           // 释放资源
           transaction.commit();
           session.close();
      }

hibernate的查询优化–抓取策略 和 延迟加载

  1. 原因:hibernate的关联查询(级联),非常优美。但严重的影响性能。为了提高性能,hibernate增加了补丁,即抓取策略。

  2. 抓取策略:只针对如何获取(查询)关联对象进行优化。

    • 项目的实际需求中,90%以上的sql操作–查询。所以,数据库专门创建了查询神器:索引。
      索引的好处:提高查询的效率;缺陷:降低增加、删除、修改的效率。
  3. 抓取策略,需要配合延迟加载一起使用。

  4. 抓取策略:注重发送的sql语句形式。
    延迟加载注重:什么时候,发送sql语句。

了解抓取策略:
1. 抓取策略是什么
a. 简单而言:hibernate如何获取关联对象的方法。
b. 是hibernate提升性能的一种方法。
c. 使用抓取策略的时候,延迟加载也会影响查询性能。所以,抓取策略和延迟加载,是配合使用。

  1. 为什么学习抓取策略
    • 项目开发中,查询无处不在。但hibernate本身的查询效率不好。加上hibernate有关联机制(1对多关系,多对多关系),什么时候获取关联对象,如何获取关联对象,对查询性能的影响很大。所以,hibernate通过抓取策略,来对关联对象的查询,进行优化。

hibernate的延迟加载:延迟加载可以针对类和关联对象

  1. 延迟加载定义

    • 延迟加载,也成为懒加载,只有真正需要数据的时候,才真正执行数据加载操作(发送sql查询语句)。
  2. 延迟加载的分类
    a. 类级别的延迟加载
    (1) 查询某个对象时,是否延迟,标签上配置lazy属性,默认值是:true,默认延迟加载。
    b. 关联级别的延迟加载。
    (1) 查询一个对象的关联对象时,是否延迟。在或上配置延迟。
    (2) hibernate关联对象默认采用延迟加载。可以避免一些不必要的性能开销

关联对象的抓取策略和延迟加载 – 批量抓取

一)set标签 中配置fetch抓取策略和lazy延迟加载
1)set标签 中配置fetch抓取策略和lazy延迟加载
a. fetch的取值:抓取策略
(1) select.默认值,发送普通的select语句,用到什么查什么
(2) join:发送迫切左外联接去查询。一次性全部查询完
(3) subselect。发送一条子查询语句查询其关联对象.(查询多个对象,才能体现),配置了之后,要查询多个关联对象的时候,只发送一条sql语句将所有关联对象查询出来。

 b. lazy的取值:
    (1) true。默认值,采用延迟加载
    (2) false.不采用延迟加载
    (3) extra. 极其懒惰的

2)理解fetch和lazy的取值
a. fetch,配置抓取策略。不同的配置,在查询关联对象时,发送的SQL语句不同。
b. lazy,配置延迟加载。不同的配置,在查询关联对象时,发送SQL语句的时间不同。(立即发送,还是延迟发送)

3)详解
(1) fetch和lazy,关注都是查询关联对象。
(2) fetch=”join”,发送迫切左外连接,把当前对象及其关联对象,一条SQL语句,全部获取。无需再次发送SQL语句,进行关联对象查询,也就无所谓延迟加载的设定(lazy失效)
(3) fetch=”subselect”,需要查询多个对象,才能体现。
(4) lazy=”extra”,意味着“极其懒惰的”,

二)many-to-one上的fetch和lazy
1. 验证的场景
* 查询某个员工,及该员工所在部门的名称

  1. many-to-one标签
    a. fetch的取值:
    (1) select.默认值,发送普通的select语句
    (2) join:发送迫切左外联接去查询
    b. lazy的取值:
    (1) proxy。默认值,是否延迟取决于一的一方类上lazy属性
    (2) false.不采用延迟加载
    (3) no-proxy:不用研究

三)关联对象的抓取策略:批量抓取
1. 感受批量抓取
a. 场景1:查询所有部门,并查询每个部门的所有员工的名字
* 在set上配置:batch-size=”3”

b. 场景2:查询所有员工,并查询其对应部门的名称
    * batch-size必须配置在一的一方的标签上

案例:
理解配置subselect策略的时候的现象:

      @Test
      public void test02(){
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Criteria criteria = session.createCriteria(Department.class);
           List list = criteria.list();
           for (Department object : list) {
                 System.out.println(object.toString());
                 System.out.println(object.getEmployees().size());
           }

           transaction.commit();
           session.close();
      }

配置为select抓取策略的时候的sql语句:
有多少条记录,执行多少条sql语句进行查询数据库

Hibernate:
    select
        this_.did as did1_0_0_,
        this_.dname as dname2_0_0_
    from
        department this_
Hibernate:
    select
        employees0_.e_did as e_did4_1_0_,
        employees0_.eid as eid1_1_0_,
        employees0_.eid as eid1_1_1_,
        employees0_.ename as ename2_1_1_,
        employees0_.sex as sex3_1_1_,
        employees0_.e_did as e_did4_1_1_
    from
        employee employees0_
    where
        employees0_.e_did=?
Hibernate:
    select
        employees0_.e_did as e_did4_1_0_,
        employees0_.eid as eid1_1_0_,
        employees0_.eid as eid1_1_1_,
        employees0_.ename as ename2_1_1_,
        employees0_.sex as sex3_1_1_,
        employees0_.e_did as e_did4_1_1_
    from
        employee employees0_
    where
        employees0_.e_did=?
Hibernate:
    select
        employees0_.e_did as e_did4_1_0_,
        employees0_.eid as eid1_1_0_,
        employees0_.eid as eid1_1_1_,
        employees0_.ename as ename2_1_1_,
        employees0_.sex as sex3_1_1_,
        employees0_.e_did as e_did4_1_1_
    from
        employee employees0_
    where
        employees0_.e_did=?
Hibernate:
    select
        employees0_.e_did as e_did4_1_0_,
        employees0_.eid as eid1_1_0_,
        employees0_.eid as eid1_1_1_,
        employees0_.ename as ename2_1_1_,
        employees0_.sex as sex3_1_1_,
        employees0_.e_did as e_did4_1_1_
    from
        employee employees0_
    where
        employees0_.e_did=?

配置为subselect抓取策略时候的语句:

Hibernate:
    select
        this_.did as did1_0_0_,
        this_.dname as dname2_0_0_
    from
        department this_
Hibernate:
    select
        employees0_.e_did as e_did4_1_1_,
        employees0_.eid as eid1_1_1_,
        employees0_.eid as eid1_1_0_,
        employees0_.ename as ename2_1_0_,
        employees0_.sex as sex3_1_0_,
        employees0_.e_did as e_did4_1_0_
    from
        employee employees0_
    where
        employees0_.e_did in (
            select
                this_.did
            from
                department this_
        )

你可能感兴趣的:(java学习随记)