Hibernate之檢索方式(HQL/QBC/本地SQL)

一、概述

  • Hibernate提供了以下幾種檢索對象的方式

    • 導航對象圖:根據已經加載的對象導航到其它對象

    • OID:按照對象的OID來檢索對象

    • HQL:使用面向對象的HQL查詢語句

    • QBC:使用QBC(Query By Criteria)API來檢索對象

    • 本地SQL:使用本地數據庫的SQL查詢語句

二、HQL

  • HQL是面向對象的查詢語句,它有如下功能:

    • 在查詢語句中設定各種查詢條件

    • 支持投影查詢,即僅檢索出對象的部分屬性

    • 支持分頁查詢

    • 支持連接查詢

    • 支持分組查詢

    • 提供內置聚集函數

    • 支持子查詢

    • 支持動態綁定參數

    • 能夠調用用戶定義的SQL函數或標準的SQL函數

  • 步驟:

1.通過Session的createQuery()方法創建一個Query對象,它包括一個HQL查詢語句。HQL查詢語句中可以包含命名參數

2.動態綁定參數

3.調用Query相關方法執行查詢語句

  • Query接口支持方法鏈編程風格,它的setXxx()方法返回自身實例

  • 綁定參數

    • Hibernate的參數綁定機制依賴於JDBC API中的PreparedStatement的預定義SQL語句功能

    • HQL的參數綁定有兩種形式:

      • 按參數名字綁定:命名參數以“:”開頭

      • 按參數位置綁定:用"?"來定義參數位置

    • 相關方法:

      • setEntity():把參數與一個持久化類綁定

      • setParameter():綁定任意類型的參數,該方法的第三個參數顯示指定Hibernate映射類型

  • HQL採用ORDER BY關鍵字對查詢結果排序

實體類(對應的配置文件省略)

Department.java

public class Department {
​
    private Integer id;
    private String name;
    
    private Set<Employee> emps = new HashSet<>();
}

Employee.java

public class Employee {
​
    private Integer id;
    private String name;
    private float salary;
    private String email;
  
    
    public Employee() {}
  
    public Employee(String email, float salary, Department dept) {
        this.salary = salary;
        this.email = email;
        this.dept = dept;
    }
​
  
  ///get(),set()方法
}

測試:

    private SessionFactory sessionFactory;
    private Session session;
    private Transaction transaction;
    
    @Before
    public void init(){
        Configuration configuration = new Configuration().configure();
        ServiceRegistry serviceRegistry = 
                new ServiceRegistryBuilder().applySettings(configuration.getProperties())
                                            .buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
    }
    
    @After
    public void destroy(){
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
​
    @Test
    public void testHQLNamedParameter(){
        
        //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());  
    }
    
    @Test
    public void testHQL(){
        
        //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());  
    }

分頁查詢

  • setFirstResult(int firstResult):設定從哪一個對象開始索引(起始值爲0)

  • setMaxResults(int maxResults):設定一次最多索引出的對象的數目

    @Test
    public void testPageQuery(){
        String hql = "FROM Employee";
        Query query = session.createQuery(hql);
        //當前頁
        int pageNo = 22;
        //每頁的記錄數
        int pageSize = 5;
        
        List<Employee> emps = 
                                query.setFirstResult((pageNo - 1) * pageSize)
                                     .setMaxResults(pageSize)
                                     .list();
        System.out.println(emps);
    }

在映射文件中定義命名查詢語句

  • Hibernate允許在映射文件中定義字符串形式的查詢語句

  • <query>元素用於定義一個HQL查詢語句,它和<class>元素並列

  • 在程序中通過Session的getNamedQuery()方法獲取查詢語句對應的Query對象

    <query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]></query>
    @Test
    public void testNamedQuery(){
        Query query = session.getNamedQuery("salaryEmps");
        
        List<Employee> emps = query.setFloat("minSal", 5000)
                                   .setFloat("maxSal", 10000)
                                   .list();
        
        System.out.println(emps.size()); 
    }

投影查詢

  • 查詢結果僅包含實體的部分屬性,通過select關鍵字實現

  • list()方法返回的集合中包含的是數組類型的元素,每個對象數組代表查詢結果的一條記錄

  • 可以在持久化類中定義一個對象的構造器來包裝投影查詢返回的記錄

  • 可以通過distinct關鍵字來保證查詢結果不會返回重複元素

    @Test
    public void testFieldQuery2(){
        String hql = "SELECT new Employee(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<Employee> result = query.setEntity("dept", dept)
                                     .list();
        
        for(Employee emp: result){
            System.out.println(emp.getId() + ", " + emp.getEmail() 
                    + ", " + emp.getSalary() + ", " + emp.getDept());
        }
    }
    
    @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<Object[]> result = query.setEntity("dept", dept)
                                     .list();
        
        for(Object [] objs: result){
            System.out.println(Arrays.asList(objs));
        }
    }

報表查詢

  • 報表查詢用於對數據分組和統計

  • 在HQL查詢語句中可以調用以下聚集函數

    • count()

    • min()

    • max()

    • sum()

    • avg()

    @Test
    public void testGroupBy(){
        String hql = "SELECT min(e.salary), max(e.salary) "
                + "FROM Employee e "
                + "GROUP BY e.dept "
                + "HAVING min(salary) > :minSal";
        
        Query query = session.createQuery(hql)
                             .setFloat("minSal", 8000);
        
        List<Object []> result = query.list();
        for(Object [] objs: result){
            System.out.println(Arrays.asList(objs));
        }
    }

迫切左外連接

  • 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();
        depts = new ArrayList<>(new LinkedHashSet(depts));
        System.out.println(depts.size()); 
        
        for(Department dept: depts){
            System.out.println(dept.getName() + "-" + dept.getEmps().size());
        }
    }

左外連接:

  • 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()); 
        }
        
//      String hql = "FROM Department d LEFT JOIN d.emps";
//      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));
//      }
    }

(迫切)內連接(原理同上)

    @Test
    public void testLeftJoinFetch2(){
        String hql = "SELECT e FROM Employee e INNER JOIN e.dept";
        Query query = session.createQuery(hql);
        
        List<Employee> emps = query.list();
        System.out.println(emps.size()); 
        
        for(Employee emp: emps){
            System.out.println(emp.getName() + ", " + emp.getDept().getName());
        }
    }

三、QBC

  • QBC查詢就是通過使用Hibernate提供的QBC API來查詢對象

    @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();
    }
    
    @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 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));
       //name like "%a%" and dept=Department [id=80]
        System.out.println(conjunction); 
        
        //2. OR
        Disjunction disjunction = Restrictions.disjunction();
        disjunction.add(Restrictions.ge("salary", 6000F));
        disjunction.add(Restrictions.isNull("email"));
        //salary>=6000 or email is null
        System.out.println(disjunction);    
      
       //上述的兩個條件用and連接起來
        criteria.add(disjunction);
        criteria.add(conjunction);
        
        criteria.list();
    }
    
    @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); 
    }

四、本地SQL

    @Test
    public void testNativeSQL(){
        String sql = "INSERT INTO gg_department VALUES(?, ?)";
        Query query = session.createSQLQuery(sql);
        
        query.setInteger(0, 280)
             .setString(1, "ATGUIGU")
             .executeUpdate();
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章