Hibernate 的查询

1. Hibernate 框架的查询方式

  • 唯一标识OID的检索方式: session.get(对象.class, OID)
  • 对象导航的方式;
  • HQL 检索方式;
  • QBC 检索方式;
  • SQL 检索方式

2. HQL 检索方式

2.1 HQL 与 SQL 的关系

  • HQL: Hibernate Query Language; 使用的是 Query 接口;
  • HQL 查询语句是面向对象的. Hibernate 负责解析 HQL 查询语句,然后根据对象-关系映射文件中的映射信息,把 HQL
    查询语句翻译成相应的 SQL 语句;
  • HQL 查询语句中的主体是域模型中的类及类的属性;
  • SQL 查询语句中的主体是数据库表及表的字段;

2.2 HQL 查询所有

// 查询所有客户
    // 第一种方式:
    // 创建 HQL 的查询接口(即 Query 接口)
    Query query = session.createQuery("from Customer");
    List<Customer> list = query.list();

    // 第二种方式: 方法链
    List<Customer> list = session.createQuery("from Customer").list();

    // 第三种方式: 使用别名的方式
    List<Customer> list = session.createQuery("from Customer c").list();
    List<Customer> list = session.createQuery("select c from Customer c").list();

2.3 HQL 条件查询

// 第一种方式:
    Query query = session.createQuery("from Customer where name=?");
    query.setString(0,"张三");  // 给第一个问号赋值
    List<Customer> list = query.list();

// 第二种方式:
    Query query = session.createQuery("from Customer where name= :aaa and age= :bbb");
    query.setString("aaa","张三");
    query.setInteger("bbb",39);
    List<Customer> list = query.list();

// 第三种方式:
    Query query = session.createQuery("from Customer where name=?");
    query.setParameter(0,"张三");  // "setParameter" 不用考虑参数的具体类型;

2.4 HQL 排序查询

// 升序
    session.createQuery("from Customer order by cust_id").list();

// 降序
    session.createQuery("from Customer order by cust_id desc").list();

2.5 HQL 分页查询

// Hibernate 框架提供了关于分页的两个方法
    // setFirstResult(a)  : 从哪条记录开始,如果查询是从第一条记录开始, 值是 0;
    // setMaxResults(b) : 每页查询的记录条数;

    List<LinkMan> list = session.creatQuery("from Customer").setFirstResult(0)
                                                            .setMaxResults(10).list();

2.6 HQL 投影查询

  • 投影查询就是想查询某一字段或某几个字段的值;
// 需求: 查询多个字段
// 第一种方式: 返回的是由这几个字段组成的数组
    List<Object[]> list = session.createQuery("select c.cust_name,c.cust_level from Customer c").list();
    for(Object[] objects : list){
        System.out.println(Arrays.toString(objects));
    }


// 第二种方式: 返回只有这几个字段组成的对象
    // 1. 需要先在持久化类中提供对应字段的有参构造方法;
    // 2. 通过 new 将多个字段封装到对象中, 进行查询;

    public class Customer{
        private String cust_name;
        private Integer cust_age;
        private String cust_level;
        ...

        // 有参构造方法
        public Customer(String cust_name, String cust_level){
            this.cust_name = cust_name;
            this.cust_level = cust_level;
        }

        // 保留空的构造方法
        public Customer(){
            super();
        }

        .....get 和 set 方法

    }

    List<Customer> list = session.createQuery("select 
            new Customer(c.cust_name, c.cust_level)  from Customer c").list();
    for(Customer customer : list){
        System.out.println(customer);
    }

2.7 HQL 聚合函数

  • count()
  • sum()
  • avg()
  • min()
  • max()
// 1. 获取总的记录数
    // List<Number> list = session.createQuery("select count(*) from Customer").list();
    List<Number> list = session.createQuery("select count(c) from Customer c").list();
    Long count = list.get(0).longValue();
    System.out.println(count);

// 2. 获取某一列数据的和
 List<Number> list = session.createQuery("select sum(c.cust_id) from Customer c").list();
    Long count = list.get(0).longValue();
    System.out.println(count);

3. QBC 检索方式

3.1 QBC 概述

  • QBC,Query By Criteria, 使用 Criteria 接口;
  • 完全面向对象;
  • 非常适合做条件查询;

3.2 QBC 查询所有

// 查询所有记录
    Criteria criteria = session.createCriteria(Customer.class);
    List<Customer> list = criteria.list();

3.3 排序查询

  • 使用 addOrder() 方法来设置参数; org.hibernate.criterion.Order
    Criteria criteria = session.createCriteria(Customer.class);
    // 设置为降序
    criteria.addOrder(Order.desc("cust_id"));
    List<Customer> list = criteria.list();

3.4 分页查询

  • setFirstResult()
  • setMaxResults()
    Criteria criteria = session.createCriteria(Customer.class);
    // 设置为降序
    criteria.addOrder(Order.desc("cust_id"));

    // 设置分页的方法
    criteria.setFirstResult(0);
    criteria.setMaxResults(10);

    List<Customer> list = criteria.list();

3.5 QBC 条件查询

  • 条件查询使用 Criteria 接口的 add 方法,用来传入条件;
  • Criterion 接口,表示查询的条件;
  • Restrictions 类是 Hibernate 框架提供的工具类,不是 Criterion 接口的实现类;用来设置查询条件;
  • Restrictions 中常用方法:
    • eq(等于), gt(大于), lt(小于)
    • ge(大于等于), le(小于等于)
    • between: 在...之间, 头和尾都包含;
    • like: 模糊查询;
    • in: 范围;
    • and: 并且;
    • or: 或者;
    • isNull: 判断值是否为空;
    • isEmpty: 判断关联的集合是否为空;
// 查询姓名叫"张三"的客户
    Criteria criteria = session.createCriteria(Customer.class);

    // 使用 Restrictions 传入条件
    criteria.add(Restrictions.eq("name","张三"));
    List<Customer> list = criteria.list();

// 查询姓名中包含"小"的客户(模糊查询)
    Criteria criteria = session.createCriteria(Customer.class);
    criteria.add(Restrictions.like("name","%小%"));
    List<Customer> list = criteria.list();

    // in 方法
    // Restrictions.in(String propertyName, Collection values); // 集合
    // Restrictions.in(String propertyName, Object[] values);  // 数组

    List<Long> params = new ArrayList<Long>();
    params.add(1L);
    params.add(2L);
    params.add(7L);

    criteria.add(Restrictions.in("cust_id",params));

    // or 方法, 性别为女,或者 cust_id 大于 3
    criteria.add(Restrictions.or(Restrictions.eq("cust_gender","女"),Restrictions.gt("cust_id",3L)));

3.6 QBC 聚合函数查询

  • Projections 是 Hibernate 框架的工具类,用来设置聚合函数查询; org.hibernate.criterion包下
  • 需要使用 criteria.setProjection() 来添加条件;

    Criteria criteria = session.createCriteria(Customer.class);
    // 设置聚合函数
    criteria.setProjection(Projections.rowCount());

    List<Number> list = criteria.list();
    Long count = list.get(0).longValue();

// 注意问题
    pubic void fun(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tr = session.beginTransaction();

        // 创建 QBC 查询接口
        Criteria criteria = session.createCriteria(Customer.class);
        // 设置聚合函数
        criteria.setProjection(Projections.count("cust_id"));
        List<Number> list = criteria.list();
        Long count = list.get(0).longValue();

        // 因为此时的查询语句, 相当于 select count(cust_id) from 表
        // 如果想继续查询所有客户,需要 setProjection(null);
        criteria.setProjection(null);

        // 继续查询所有客户
        List<Customer> customers = criteria.list();
        for(Customer customer : customers){
            System.out.println(customer);
        }
    }

4. 离线条件查询

  • 离线条件查询使用的是 DetachedCriteria 接口进行查询;
  • 离线条件查询对象在创建的时候,不需要使用 session 对象, 只是在查询的时候,使用 session 对象;
  • 可以在 WEB 层创建离线条件查询对象;

    Session session = HibernateUtils.getCurrentSession();
    Transaction tr = session.beginTransaction();

    // 创建离线条件查询对象
    DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);

    // 设置查询条件
    criteria.add(Restrictions.eq("cust_gender","男"));
    // 查询数据, 需要使用 session
    List<Customer> list = criteria.getExecutableCriteria(session).list();
    for(Customer customer : list){
        System.out.println(customer);   
    }

    tr.commit();

5. SQL 查询方式(了解)

    Session session = HibernateUtils.getCurrentSession();
    Transaction tr = session.beginTransaction();

    SQLQuery sqlQuery = session.createSQLQuery("select * from cust_customer where cust_gender = ?");
    sqlQuery.setParameter(0,"男");

    // 将返回结果(是List<Object[]> 类型), 封装到对象中
    sqlQuery.addEntity(Customer.class);
    List<Customer> list = sqlQuery.list();

    for(Customer customer : list){
        System.out.println(customer);
    }

    tr.commit();

6. HQL 多表查询

6.1 SQL 的多表查询

// 内连接查询
    // 显示内连接
    select * from customer c inner join orders o on c.cid = o.cno;
    // 隐式内连接
    select * from customer c, orders o where c.cid = o.cno;

// 外连接
    // 左外连接
    select * from customer c left outer join orders o on c.cid = o.cno;
    // 右外连接
    select * from customer c right outer join orders o on c.cid = o.cno;

6.2 HQL 的多表查询

  • 非迫切连接: 返回的结果是 Object[];
  • 迫切连接: 返回的结果是对象,
// 使用内连接查询,默认返回的是 Object 数组
    Session session = HibernateUtils.getCurrentSession();
    Transaction tr = session.beginTransaction();

    List<Object[]> list = session.createQuery("from Customer c inner join c.linkmans").list();
    for(Object[] objects : list){
        System.out.println(Arrays.toString(objects));
    }

// 使用 fetch 关键字, 会把返回结果封装到对象中
// fetch 迫切连接
    List<Customer> list = session.createQuery("from Customer c inner join fetch c.linkmans").list();
    for(Customer customer : list){
        System.out.println(customer);   
    }

// 解决数据重复的问题, 因为 Customer 对象中存在 Set<Linkman> linkmans 集合
// 所以返回的结果, 一个联系人对应一个客户.
// 需要的结果: 一个客户中存在n个联系人. 自己手动 new Set 集合
    List<Customer> list = session.createQuery("from Customer c inner join fetch c.linkmans").list();
    Set<Customer> set = new HashSet<Customer>(list);

    for(Customer customer : set){
        System.out.println(customer);
    }


// 迫切左外连接
    List<Customer> list = session.createQuery("from Customer c left join fetch c.linkmans").list();

7. HQL 框架之延迟加载

  1. 延迟加载先获取到代理对象,当真正使用到该对象中的属性的时候,才会发送 SQL 语句, 是 Hibernate 框架提升性能的方式;
  2. 类级别的延迟加载
    • session 对象的 load() 方法就是延迟加载;
    • Customer c = session.load(Customer.class, 1L)
      没有发送 SQL 语句, 当使用该对象的属性时,才发送 SQL 语句
    • 使类级别的延迟加载失效(两种方法)
      • <class> 标签上配置 lazy="false";
      • Hibernate.initialize(Object proxy);
  3. 关联级别的延迟加载
    • 查询某个客户下的所有联系人, 默认是延迟加载;

8. Hibernate 框架关联级别的查询优化策略

8.1 查询策略

  • 使用 Hibernate 框架查询一个对象的时候,查询其关联对象,应该如何查询. 是 Hibernate 框架的一种优化手段;

8.2 Hibernate 框架的查询策略解决的问题

  1. 查询的时机
    • lazy属性解决查询的时机问题,表示是否需要延迟加载;
  2. 查询的语句形式
    • fetch属性解决查询语句的格式问题;

8.3 在 <set> 标签上使用 fetchlazy 属性

  1. fetch 的取值:
    • select: 默认值,发送基本select语句查询;
    • join: 连接查询,发送的是一条迫切左外连接! 配置了该属性,lazy属性就失效了;
    • subselect: 子查询,发送一条子查询,查询关联对象;
  2. lazy 的取值
    • true: 默认延迟;
    • false: 不延迟;
    • extra: 及其懒惰;
  3. 总结: 开发中, 基本上使用的都是默认值: fetch = select, lazy=true;

8.4 在 <many-to-one> 标签上使用 fetchlazy 属性

  1. fetch 的取值
    • select: 默认值,发送基本select语句查询;
    • join: 发送迫切左外连接查询;
  2. lazy 的取值
    • false: 不采用延迟加载;
    • proxy: 默认值,代理;表示由另一端 <class> 上的 lazy 属性决定;

参考资料

原文地址:https://www.cnblogs.com/linkworld/p/7704939.html