1. 程式人生 > >Hibernate 學習筆記(而)—— Hibernate HQL查詢和 QBC 查詢

Hibernate 學習筆記(而)—— Hibernate HQL查詢和 QBC 查詢

getc 顯示 delete 取數 vat ESS 轉化 靈活 ger

目錄

  • 一、Hibernate 的 HQL 查詢
    • 1.1、查詢所有數據
    • 1.2、條件查詢
    • 1.3、排序查詢
    • 1.4、統計查詢
    • 1.5、分頁查詢
    • 1.6、投影查詢
  • 二、Hibernate 的 QBC 查詢
    • 2.1、基本查詢
    • 2.2、條件查詢
    • 2.3、排序查詢
    • 2.4、統計查詢
    • 2.5、分頁查詢
    • 2.6、投影查詢
    • 2.7、離線查詢

在 Hibernate 中,查詢方式有 HQL 和 Criteria 查詢兩種方式,HQL是Hibernate Query Language的縮寫,語法類似於 SQL 語句,可以直接使用實體類名稱及屬性名稱來查詢,它提供更加豐富靈活、更為強大的查詢能力。

Criteria 查詢對查詢條件進行了面向對象封裝,符合編程人員的思維方式,不過HQL(Hibernate Query Language)查詢提供了更加豐富的和靈活的查詢特性,因此 Hibernate將 HQL 查詢方式立為官方推薦的標準查詢方式,HQL 查詢在涵蓋 Criteria 查詢的所有功能的前提下,提供了類似標準 SQL 語句的查詢方式,同時也提供了更加面向對象的封裝。

一、Hibernate 的 HQL 查詢

HQL 語法類似於 SQL,有 SQL 的關鍵詞如 select 、from 、order by 、count()、where 等,完整的HQL語句形式如下: Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc。

Hibernate 的 HQL 查詢中,通過 Session 對象的 createQuery(String HQL) 方法,獲取 Query 對象, Query 中傳入查詢的 HQL 語句,之後通過 Query對象的 list() 方法,獲取查詢的結果集。

HQL 書寫規範:

  1. 在查詢語句中省略 select 關鍵字
  2. 使用類名稱替代數據庫中的表名稱
  3. 使用實體類的屬性名稱代替數據庫表中的列名稱

HQL 查詢步驟:

  1. Query query = session.createQuery(String HQL);:獲取查詢的 Query 對象
  2. query.setParameter(arg0,arg1);:設置查詢條件參數,參數下標從0開始
  3. List list = query.list();:獲取查詢結果集

1.1、查詢所有數據

package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {

    // 查詢數據庫表中的所有數據
    @Test
    public void testHQL01() {

        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        // 使用session的createQuery(String HQL)獲取 Query 對象
        Query query = session.createQuery("from User");
        // 使用 query對象的list()方法,獲取查詢結果集
        List<User> list = query.list();

        for (User user : list) {
            System.out.println(user);
        }

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

1.2、條件查詢

在條件查詢中,HQL 書寫方式與 SQL 一樣,使用 ? 作為參數的占位符,通過 setParameter(arg0, arg1) 來設定參數的值,其中:

  • arg0:表示參數的小標,從 0 開始;
  • arg1:表示參數的具體值;
package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {

    // 條件查詢,查詢昵稱裏面有螞蟻的用戶
    @Test
    public void testHQL02() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        // 使用session的createQuery(String HQL)獲取 Query 對象
        Query query = session.createQuery("from User where nickname like ?");
        // 使用模糊查詢
        query.setParameter(0, "%螞蟻%");
        // 使用 query對象的list()方法,獲取查詢結果集
        List<User> list = query.list();
        
        for (User user : list) {
            System.out.println(user);
        }

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

1.3、排序查詢

在排序查詢中,HQL 書寫方式與 SQL 一樣,使用 order by 屬性名稱 desc/asc 來進行排序。

package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {
    
    // 排序查詢
    @Test
    public void testHQL03() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Query query = session.createQuery("from User order by uid desc");
        
        List<User> list = query.list();

        for (User user : list) {
            System.out.println(user);
        }

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

1.4、統計查詢

在 Hibernate 中,使用 HQL 語句,也可以使用聚合函數 (count()、avg()、sum()、max()、min())進行查詢。使用聚合函數式, HQL 語句的寫法如下:

Query query = session.createQuery("select count(*) from User");

package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {
    
    // 統計查詢
    @Test
    public void testHQL04() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Query query = session.createQuery("select count(*) from User");

        // 使用聚合函數中,除了使用 group
        // by進行分組的情況下,返回的都是唯一的結果,此時可以用query的uniqueResult()方法,接收唯一結果
        // 使用 Long 類型進行接收
        Long count = (Long) query.uniqueResult();
        System.out.println(count);
    }
}

1.5、分頁查詢

在 Hibernate 的分頁查詢中,通過 query.setFirstResult(0)query.setMaxResults(2) 方法分別設置查詢的開始位置和每頁顯示的數量,從而達到分頁查詢的效果。

  1. query.setFirstResult(0):設置查詢的開始位置,從0開始
  2. query.setMaxResults(2):設置每頁的顯示數據量
package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {

    // 分頁查詢
    @Test
    public void testHQL05() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();
        
        Query query = session.createQuery("from User");
        
        query.setFirstResult(0);
        query.setMaxResults(2);

        List<User> list = query.list();

        for (User user : list) {
            System.out.println(user);
        }

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

1.6、投影查詢

在 Hibernate 框架中,當我們只需要查出某個對象中的個別屬性的時候,如目前只需要查出用戶的用戶名和密碼兩個屬性,如果使用 HQL 查詢的方式,將會查找出該對象的全部屬性,此時將會降低查詢的速度和浪費系統的資源。

如果我們使用Query query = session.createQuery(select username , password from User); 此時通過 query.list(); 方法,我們得到的結果集是 List<Object[]> 類型的,不符合面向對象的特性,而且結果集不方便使用。

為了解決這一問題,Hibernate 也給出了解決方案投影查詢,通過 Query query = session.createQuery("select new User(username,password ) from User"); 此時,通過 query.list() 方法得到的結果集是 list<User> 類型的,但是其中 User 對象除 usernamepassword 以外的屬性均為 null

需要註意的是:在使用投影查詢中的 HQL 語句中,出現了 new 關鍵字,這意味著,我們需要在普通Java類中添加所要查詢屬性的有參構造函數,此外,為了保證該普通 Java 類符合 JavaBean 的規範,我們需要聲明其無參構造函數,不然程序運行時會報出 org.hibernate.hql.ast.QuerySyntaxException 異常。

User 實體類

package com.hibernate.domain;

import java.util.Date;

public class User {
    
    private Integer uid;
    private String username;
    private String password;
    private String nickname;
    private String realname;
    private Date birthday;
    
    public Integer getUid() {
        return uid;
    }
    public void setUid(Integer uid) {
        this.uid = uid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getNickname() {
        return nickname;
    }
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
    public String getRealname() {
        return realname;
    }
    public void setRealname(String realname) {
        this.realname = realname;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    
    public User() {}
    
    @Override
    public String toString() {
        return "User [uid=" + uid + ", username=" + username + ", password=" + password + ", nickname=" + nickname
                + ", realname=" + realname + ", birthday=" + birthday + "]";
    }
    
}

投影查詢:

package com.hibernate.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestHQL {

    // 投影查詢:查詢用戶名和密碼
    @Test
    public void testHQL06() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();
        
        Query query = session.createQuery("select new User(username, password) from User");
        // 所查詢得到的結果集仍然是List<User> 類型
        List<User> list = query.list();

        for (User user : list) {
            System.out.println(user);
        }

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

二、Hibernate 的 QBC 查詢

Hibernate 中的 QBC 查詢方式,全稱為 Query By Criteria,通過 Session 對象的 createCriteria(Class clazz) 方法創建 Criteria 對象,之後通過 Criteria 對象的 list() 方法獲取查詢結果集。它是一種面向對象的查詢方式,QBC 查詢把生成語句的過程全部融入到方法中了。

2.1、基本查詢

package com.hibernate.test;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    
    // 基本查詢
    @Test
    public void testQBC01() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();
        // 使用 createCriteria(Class clazz) 創建 Criteria 對象
        Criteria criteria = session.createCriteria(User.class);
        List<User> list = criteria.list();
        for (User user : list) {
            System.out.println(user);
        }
        
        tx.commit();
        session.close();
    }
}

2.2、條件查詢

Hibernate QBC 查詢進行條件查詢時候,通過 Criteriaadd() 方法,添加查詢條件。

package com.hibernate.test;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    
    // 條件查詢,通過 add()方法添加查詢條件
    @Test
    public void testQBC02() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 通過 Restrictions 添加具體的條件
        criteria.add(Restrictions.like("nickname", "%螞蟻%"));
        List<User> users = criteria.list();
        for (User user : users) {
            System.out.println(user);
        }

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

2.3、排序查詢

Hibernate QBC 查詢進行排序查詢時候,通過 CriteriaaddOrder() 方法,添加排序查詢的條件。addOrder(Order.asc(屬性名稱))進行升序查詢和 addOrder(Order.desc(屬性名稱)) 進行降序查詢

package com.hibernate.test;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    // 排序查詢
    @Test
    public void testQBC03() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 通過 addOrder() 添加排序條件
        criteria.addOrder(Order.desc("uid"));
        List<User> users = criteria.list();
        for (User user : users) {
            System.out.println(user);
        }

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

2.4、統計查詢

QBC 查詢中,Criteria 通過 criteria.setProjection(Projections.XXX) 方法來設定聚合函數。如使用 Projections.rowCount() 獲取數據表中的總記錄數,通過 Projections.count("實體類屬性名稱") 獲取該屬性有屬性值的數據條記錄數。

package com.hibernate.test;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    // 統計查詢
    @Test
    public void testQBC04() {

        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 獲取數據庫表中具有uid屬性值的數據條數
        criteria.setProjection(Projections.count("uid"));

        Long count = (Long) criteria.uniqueResult();
        System.out.println(count);
        
        tx.commit();
        session.close();    
    }
}

2.5、分頁查詢

QBC 和 HQL 中的分頁查詢所用的方法和方法的含義是一模一樣的。通過 query.setFirstResult(0)query.setMaxResults(2) 方法分別設置查詢的開始位置和每頁顯示的數量,從而達到分頁查詢的效果。

  1. query.setFirstResult(0):設置查詢的開始位置,從0開始
  2. query.setMaxResults(2):設置每頁的顯示數據量
package com.hibernate.test;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    // 分頁查詢
    @Test
    public void testQBC05() {

        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 設置分頁條件
        criteria.setFirstResult(0);
        criteria.setMaxResults(2);

        List<User> users = criteria.list();
        for (User user : users) {
            System.out.println(user);
        }

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

2.6、投影查詢

package com.hibernate.test;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    
    // 投影查詢
    @Test
    public void testQBC06() {

        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        tx.begin();

        Criteria criteria = session.createCriteria(User.class);
        // 通過Projections.projectionList().add()添加所要查詢的屬性名
        criteria.setProjection(
Projections.projectionList().add(Property.forName("username")).add(Property.forName("password")));

        List<User> users = criteria.list();
        for (User user : users) {
            System.out.println(user);
        }

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

2.7、離線查詢

技術分享圖片

在 JavaWeb 項目中,客戶端查詢實體總共經以下步驟:

  1. 客戶端提交查詢條件;
  2. 表現層 servlet 接收到客戶端提交的查詢條件,開啟 SessionTransaction,創建 Criteria 對象,將查詢條件封裝到所創建的 criteria 對象中,並將 criteria 對象傳遞到業務層;
  3. 業務層將接收的 criteria 對象傳遞給持久層;
  4. 持久層接收 criteria 對象,查詢數據庫,並將查詢結果依次傳遞給業務層、表現層,最後傳遞到客戶端;

存在問題:

此時發現,在表現層中出現了開啟 SessionTransaction,創建 Criteria 對象等屬於持久層的操作,不符合 MVC 的程序設計規範,對後期程序的維護造成壓力;

解決方式:使用 QBC 查詢的離線查詢,創建 DetachedCriteria 對象,該對象的獲取不需要 Session,可以直接獲得。

技術分享圖片

  1. 客戶端提交查詢條件,
  2. 表現層 servlet 接收到客戶端提交的查詢條件,創建 DetachedCriteria 對象,封裝查詢條件;並將 criteria 對象傳遞到業務層;
  3. 業務層將接收的 DetachedCriteria 對象傳遞給持久層;
  4. 持久層接收 DetachedCriteria 對象,將其轉化成為 Criteria 對象,查詢數據庫,並將查詢結果依次傳遞給業務層、表現層,最後傳遞到客戶端;
package com.hibernate.test;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.hibernate.domain.User;
import com.hibernate.utils.HibernateUtil;

public class TestQBC {
    
    // 模擬表現層 —— Servlet
    @Test
    public void servletDetachedCriteria() {
        // 創建DetachedCriteria對象
        DetachedCriteria dc = DetachedCriteria.forClass(User.class);
        // 封裝客戶端提交的查詢條件
        dc.add(Restrictions.like("nickname", "%螞蟻%"));
        // 將封裝好查詢條件的DetachedCriteria對象傳遞給業務層
        List<User> users = seviceDetachedCriteria(dc);
        for (User user : users) {
            System.out.println(user);
        }
    }
    
    // 模擬業務層代碼
    public List seviceDetachedCriteria(DetachedCriteria dc) {
        Session session = null;
        Transaction tx = null;
        try {
            // 獲取與當前線程綁定的 Session 對象
            session = HibernateUtil.getCurrentSession();
            tx = session.beginTransaction();
            tx.begin();
            return daoDetachedCriteria(dc);
        } catch (Exception e) {
            tx.rollback();
        } finally {
            tx.commit();
        }
        return null;

    }

    // 模擬持久層代碼
    public List daoDetachedCriteria(DetachedCriteria dc) {

        // 獲取當前線程綁定的session
        Session session = HibernateUtil.getCurrentSession();
        // 將業務層傳遞的DetachedCriteria對象轉化為Criteria對象
        Criteria criteria = dc.getExecutableCriteria(session);
        // 返回查詢結果集
        return criteria.list();
    }
}

Hibernate 學習筆記(而)—— Hibernate HQL查詢和 QBC 查詢