Hibernate(六)---检索方式

(一)HQL检索方式
(1)HQL概述
· HQL(Hibernate Query Language) 是面向对象的查询语言, 它和 SQL 查询语言有些相似. 在 Hibernate 提供的各种检索方式中, HQL 是使用最广的一种检索方式. 它有如下功能:

  • 在查询语句中设定各种查询条件
  • 支持投影查询, 即仅检索出对象的部分属性
  • 支持分页查询
  • 支持连接查询
  • 支持分组查询, 允许使用 HAVING 和 GROUP BY 关键字
  • 提供内置聚集函数, 如 sum(), min() 和 max()
  • 支持子查询
  • 支持动态绑定参数
  • 能够调用 用户定义的 SQL 函数或标准的 SQL 函数

(2)HQL检索方式的的步骤:
①通过 Session 的 createQuery() 方法创建一个 Query 对象, 它包括一个 HQL 查询语句. HQL 查询语句中可以包含命名参数;
②动态绑定参数;
③调用 Query 相关方法执行查询语句.
注:
I Qurey 接口支持方法链编程风格, 它的 setXxx() 方法返回自身实例, 而不是 void 类型;

  List<Employee> employees = query.setFloat("sal", 5691)
                                  .setString("emp", "%t%")
                                  .setEntity("dep", dept)
                                  .list();

II HQL vs SQL:

  • HQL 查询语句是面向对象的, Hibernate 负责解析 HQL 查询语句, 然后根据对象-关系映射文件中的映射信息, 把 HQL 查询语句翻译成相应的 SQL 语句. HQL 查询语句中的主体是域模型中的类及类的属性.
  • SQL 查询语句是与关系数据库绑定在一起的. SQL 查询语句中的主体是数据库表及表的字段.

(3)绑定参数:
1) Hibernate 的参数绑定机制依赖于 JDBC API 中的 PreparedStatement 的预定义 SQL 语句功能.
2) HQL 的参数绑定由两种形式:
①按参数名字绑定: 在 HQL 查询语句中定义命名参数, 命名参数以 “:” 开头.

/1. 创建 Query 对象
        //基于位置的参数. 
        String hql = "FROM Employee e WHERE e.salary > ? AND e.email LIKE ? AND e.dept = ? "
                + "ORDER BY e.salary";
        Query query = session.createQuery(hql);

        //2. 绑定参数
        //Query 对象调用 setXxx 方法支持方法链的编程风格.
        Department dept = new Department();
        dept.setId(80); 
        query.setFloat(0, 6000)
             .setString(1, "%A%")
             .setEntity(2, dept);

        //3. 执行查询
        List<Employee> emps = query.list();
        System.out.println(emps.size());  

②按参数位置绑定: 在 HQL 查询语句中用 “?” 来定义参数位置

    //1. 创建 Query 对象
        //基于命名参数. 
        String hql = "FROM Employee e WHERE e.salary > :sal AND e.email LIKE :email";
        Query query = session.createQuery(hql);

        //2. 绑定参数
        query.setFloat("sal", 7000)
             .setString("email", "%A%");

        //3. 执行查询
        List<Employee> emps = query.list();
        System.out.println(emps.size());  

注:HQL 采用ORDER BY 关键字对查询结果排序.
(4)分页查询

  • setFirstResult(int firstResult): 设定从哪一个对象开始检索, 参数 firstResult 表示这个对象在查询结果中的索引位置, 索引位置的起始值为 0. 默认情况下,Query 从查询结果中的第一个对象开始检索
  • setMaxResults(int maxResults): 设定一次最多检索出的对象的数目. 在默认情况下, Query 和Criteria 接口检索出查询结果中所有的对象
    /**
     * 分页查询
     */
    @Test
    public void testPageQuery(){
        String hql = "FROM Employee";
        int pageNo = 2;
        int pageSize = 3;
        Query query = session.createQuery(hql);

        List<Employee> employees = query.setFirstResult((pageNo - 1) * pageSize)
     .setMaxResults(pageSize)                                       
     .list();                                       
    }

(5)在映射文件中定义命名查询语句
- Hibernate 允许在映射文件中定义字符串形式的查询语句.
- <query> 元素用于定义一个 HQL 查询语句, 它和 <class> 元素并列.

<query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]></query>
  • 在程序中通过 Session 的 getNamedQuery() 方法获取查询语句对应的 Query 对象.
/**
     * 在配置文件中定义命名查询
     */
    @Test
    public void testNameQuery(){
        Query query = session.getNamedQuery("salary");
        List<Employee> employees = query.setFloat("minsal", 3000)
                                        .setFloat("maxsal", 9000).list();
        System.out.println(employees.size());
    }

(6)投影查询
- 投影查询: 查询结果仅包含实体的部分属性. 通过 SELECT 关键字实现.
- Query 的 list() 方法返回的集合中包含的是数组类型的元素, 每个对象数组代表查询结果的一条记录

    @Test
    public void testFieldQuery(){
        String hql = "SELECT e.email, e.salary, e.dept FROM Employee e WHERE e.dept = :dept";
        Query query = session.createQuery(hql);

        Department dept = new Department();
        dept.setId(80);
        //list() 方法返回的集合中包含的是数组类型的元素
        List<Object[]> result = query.setEntity("dept", dept)
                                     .list();

        for(Object [] objs: result){
            System.out.println(Arrays.asList(objs));
        }
    }
  • 可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录, 使程序代码能完全运用面向对象的语义来访问查询结果集.
    @Test
    public void testFieldQuery2(){
        String hql = "select new Employee(e.name,e.salary,e.dept) from Employee e where e.dept = :dept";
        Query query = session.createQuery(hql);
        Deparment dept = new Deparment();
        dept.setId(2);
        /*
        List<Object[]> result = query.setEntity("dept",dept).list();
        for(Object[] objs : result){
            System.out.println(Arrays.asList(objs));
        }*/
        // list()方法返回的是一个对象
        List<Employee> result = query.setEntity("dept", dept).list();
        for(Employee emp:result){
            System.out.println(emp.getName() 
                    +","+emp.getSalary()+","+emp.getDeparment());
        }
    }
  • 可以通过 DISTINCT 关键字来保证查询结果不会返回重复元素
    (7)报表查询
  • 报表查询用于对数据分组和统计, 与 SQL 一样, HQL 利用 GROUP BY 关键字对数据分组, 用 HAVING关键字对分组数据设定约束条件.

  • 在 HQL 查询语句中可以调用聚集函数[count(),min(),max(),sum(),avg()]

    /**
     * 报表查询
     */
    @Test
    public void testGroupBy(){

        String hql = "select avg(e.salary) from Employee e group by e.dept having e.dept = :dept";
        Deparment dept = new Deparment();
        dept.setId(1);

        Query query = session.createQuery(hql);

        List<Object> result = query.setEntity("dept", dept).list();
        for(Object objs : result){
        System.out.println(objs);
        }
    }

(8)HQL迫切左外连接

  • LEFT JOIN FETCH 关键字表示迫切左外连接检索策略.
  • list() 方法返回的集合中存放实体对象的引用, 每个 Department 对象关联的 Employee 集合都被初始化, 存放所有关联的 Employee 的实体对象.
  • 查询结果中可能会包含重复元素, 可以通过一个 HashSet 来过滤重复元素.
@Test
    public void testLeftJoinFetch(){
//      String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps";
        String hql = "FROM Department d INNER JOIN FETCH d.emps";
        Query query = session.createQuery(hql);

        List<Department> depts = query.list();
        //可以通过一个 HashSet 来过滤重复元素

        depts = new ArrayList<>(new LinkedHashSet(depts));
        System.out.println(depts.size()); 

        for(Department dept: depts){
            System.out.println(dept.getName() + "-" + dept.getEmps().size());
        }
    }

(9)HQL 左外连接

  • LEFT JOIN 关键字表示左外连接查询.
  • list() 方法返回的集合中存放的是对象数组类型.
  • 根据配置文件来决定 Employee 集合的检索策略.
  • 如果希望 list() 方法返回的集合中仅包含 Department 对象, 可以在HQL 查询语句中使用 SELECT 关键字.
    @Test
    public void testLeftJoin(){
        String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN d.emps";
        Query query = session.createQuery(hql);

        List<Department> depts = query.list();
        System.out.println(depts.size());

        for(Department dept: depts){
            System.out.println(dept.getName() + ", " + dept.getEmps().size()); 
        }

//      List<Object []> result = query.list(); 
//      result = new ArrayList<>(new LinkedHashSet<>(result));
//      System.out.println(result); 
//      
//      for(Object [] objs: result){
//          System.out.println(Arrays.asList(objs));
//      }
    }

注:
①不迫切不会第一时间赋值给对方集合,还没有互相引用,独立的,每次用数组封装两个对象,放进list;
②若使用左外连接select子句,select 后 跟什么对象,list就会返回什么对象,默认返回Object[];

select d from Deparment d left join d.emps ---- 返回Department类型
from Deparment d left join d.emps---返回Object[]类型
select d.name from Deparment d left join d.emps --返回Object 类型

(10)HQL迫切内连接
- INNER JOIN FETCH 关键字表示迫切内连接, 也可以省略 INNER 关键字
- list() 方法返回的集合中存放 Department 对象的引用, 每个 Department 对象的 Employee 集合都被初始化, 存放所有关联的 Employee 对象

    @Test
    public void testInnerJoinFetch(){
        //String hql = "From Deparment d Inner join fetch d.emps";
        String hql = "select distinct d from Deparment d Inner join fetch d.emps";

        Query query = session.createQuery(hql);
        List<Deparment> result = query.list();
        result = new ArrayList<>(new LinkedHashSet(result));
        for(Deparment deparment : result){
            System.out.println(deparment);
        }
    }

(11)内连接(关注下左外连接的注)
- INNER JOIN 关键字表示内连接, 也可以省略 INNER 关键字
- list() 方法的集合中存放的每个元素对应查询结果的一条记录, 每个元素都是对象数组类型
- 如果希望 list() 方法的返回的集合仅包含 Department 对象, 可以在 HQL 查询语句中使用 SELECT 关键字

    @Test
    public void testInnerJoin(){
        String hql = "from Employee e Inner join fetch e.dept";
        Query query = session.createQuery(hql);
        List<Employee> result = query.list();
        for(Employee emp : result){
            System.out.println(emp);
        }
    }

(12)关联级别运行时的检索策略
- 如果在 HQL 中没有显式指定检索策略, 将使用映射文件配置的检索策略.
- HQL 会忽略映射文件中设置的迫切左外连接检索策略, 如果希望 HQL 采用迫切左外连接策略, 就必须在 HQL 查询语句中显式的指定它
- 若在 HQL 代码中显式指定了检索策略, 就会覆盖映射文件中配置的检索策略
(13)QBC检索
· QBC 查询就是通过使用 Hibernate 提供的 Query By Criteria API 来查询对象,这种 API 封装了 SQL 语句的动态拼装,对查询提供了更加面向对象的功能接口

@Test
    public void testQBC(){
        //1. 创建一个 Criteria 对象
        Criteria criteria = session.createCriteria(Employee.class);

        //2. 添加查询条件: 在 QBC 中查询条件使用 Criterion 来表示
        //Criterion 可以通过 Restrictions 的静态方法得到
        criteria.add(Restrictions.eq("email", "SKUMAR"));
        criteria.add(Restrictions.gt("salary", 5000F));

        //3. 执行查询
        Employee employee = (Employee) criteria.uniqueResult();
        System.out.println(employee); 
    }
        @Test
    public void testQBC2(){
        Criteria criteria = session.createCriteria(Employee.class);

        //1. AND: 使用 Conjunction 表示
        //Conjunction 本身就是一个 Criterion 对象
        //且其中还可以添加 Criterion 对象
        Conjunction conjunction = Restrictions.conjunction();
        conjunction.add(Restrictions.like("name", "a", MatchMode.ANYWHERE));
        Department dept = new Department();
        dept.setId(80);
        conjunction.add(Restrictions.eq("dept", dept));
        System.out.println(conjunction); 

        //2. OR:使用Disjunction表示
        Disjunction disjunction = Restrictions.disjunction();
        disjunction.add(Restrictions.ge("salary", 6000F));
        disjunction.add(Restrictions.isNull("email"));

        criteria.add(disjunction);
        criteria.add(conjunction);

        criteria.list();
    }
        @Test
    public void testQBC3(){
        Criteria criteria = session.createCriteria(Employee.class);

        //统计查询: 使用 Projection 来表示: 可以由 Projections 的静态方法得到
        criteria.setProjection(Projections.max("salary"));

        System.out.println(criteria.uniqueResult()); 
    }
    @Test
    public void testQBC4(){
        Criteria criteria = session.createCriteria(Employee.class);

        //1. 添加排序
        criteria.addOrder(Order.asc("salary"));
        criteria.addOrder(Order.desc("email"));

        //2. 添加翻页方法
        int pageSize = 5;
        int pageNo = 3;
        criteria.setFirstResult((pageNo - 1) * pageSize)
                .setMaxResults(pageSize)
                .list();
    }

(14)本地SQL检索

    @Test
    public void testNativeSQL(){
        String sql = "INSERT INTO gg_department VALUES(?, ?)";
        //session.createSQLQuery(sql)查询本地SQL
        Query query = session.createSQLQuery(sql);

        query.setInteger(0, 280)
             .setString(1, "ATGUIGU")
             .executeUpdate();
    }
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值