重拾Hibernate框架——查詢操作

目錄

前言

  以下案例的項目上下文在《重拾Hibernate框架——一對多關聯》、《重拾Hibernate框架——多對多關聯》這兩篇文章中提及,如無特殊說明,本文中提及的案例將基於上述項目進行運行測試。

OID查詢

  根據ID查詢記錄,調用session中的get方法進行實現。如:

User user = session.get(User.class, 1);

對象導航查詢

  查詢某個公司中所有員工的過程,使用對象導航實現。對象導航查詢適用於單向一對多關係下通過“一方”獲取“多方”的信息記錄。如:

// 獲取ID爲3的公司的所有員工
Company company = session.get(Company.class, 3);
Set<Employee> employees = company.getEmployees();
System.out.println(employees.size());

HQL查詢

  HQL(HQL,Hibernate Query Language),是Hibernate提供的一種查詢語言,HQL語法類似於SQL,不同的是HQL是一種完全面向對象的語言,能夠直接查詢實體類及屬性。而SQL語句操作的是數據庫中的表和表中的字段。

查詢所有

  如查詢所有公司記錄:

Query query = session.createQuery("from Company");
List<Company> list = query.list();
for (Company company : list) {
    System.out.println(company.getId() + "=" + company.getName());
}

條件查詢

  where

// Query query = session.createQuery("from Company where id = ? and name = ?"); // 可以取別名,如下
Query query = session.createQuery("from Company as c where c.id = ? and c.name = ?");
// 設置佔位符(?)的值,從位置0開始設置
query.setParameter(0, 1);
query.setParameter(1, "CSDN");
List<Company> list = query.list();

  like

Query query = session.createQuery("from Company where name like ?");
query.setParameter(0, "%D%");
List<Company> list = query.list();

排序查詢

// 升序(默認)
// Query query = session.createQuery("from Company order by id asc");
// 降序
Query query = session.createQuery("from Company order by id desc");
List<Company> list = query.list();

分頁查詢

(1)MySQL中實現分頁:使用關鍵字limit實現
如:

SELECT * FROM t_company LIMIT 0, 3

(2)HQL中實現分頁
  在HQL語句中,不支持limit關鍵字(因爲MySQL數據庫支持limit關鍵字,但其他數據庫不一定支持)。在Hibernate的Query對象中封裝了兩個方法實現了分頁操作。如:

Query query = session.createQuery("from Company");
// 設置分頁的開始位置
query.setFirstResult(0);
// 設置每頁的記錄數
query.setMaxResults(3);
List<Company> list = query.list();

投影查詢

  投影查詢:查詢部分字段的值,而不是查詢所有字段的值,這就是投影查詢。
  注:在HQL語句中select語句後面不能寫“*”,不支持這樣查詢所有數據。
(1)查詢單個字段的值

// 查詢單個字段的值
Query query = session.createQuery("select id from Company");
List<Object> list = query.list();
for (Object obj : list) {
    System.out.println(obj);
}

(2)查詢多個字段的值

// 查詢多個字段的值
Query query = session.createQuery("select id, name from Company");
List<Object[]> list = query.list();
for (Object[] obj : list) {
    System.out.println(obj[0] + "=" + obj[1]);
}

聚合函數的使用

  常見的聚合函數:count、sum、avg、max、min。
  這裏以count爲例,查詢公司的總記錄數,如:

Query query = session.createQuery("select count(*) from Company");
Object obj = query.uniqueResult();
// 直接輸出總記錄數
System.out.println(obj);
Query query = session.createQuery("select count(*) from Company");
Object obj = query.uniqueResult();
// 轉成int輸出
Long longObj = (Long) obj;
int count = longObj.intValue();
System.out.println(count);

  這裏需要注意的是,不能直接將obj轉成int類型:

Query query = session.createQuery("select count(*) from Company");
Object obj = query.uniqueResult();
// 錯誤方式
int count = (int) obj;
System.out.println(count);

  否則會報:java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer 錯誤。

QBC查詢

  QBC查詢,即Query By Criteria查詢,使用Criteria對象進行實現。
  使用QBC查詢,一般需要以下三個步驟:
1. 使用Session實例 的createCriteria()方法創建Criteria對象;
2. 使用工具類Restrictions的方法爲Criteria對象設置查詢條件,Order工具類的方法設置排序方式,Projections工具類的方法進行統計和分組;
3. 使用Criteria對象的list()方法進行查詢並返回結果。

查詢所有

Criteria criteria = session.createCriteria(Company.class);
List<Company> list = criteria.list();
for (Company company : list) {
    System.out.println(company.getId() + "=" + company.getName());
}

條件查詢

使用工具類Restrictions的方法爲Criteria對象設置查詢條件。

  where

Criteria criteria = session.createCriteria(Company.class);
// 添加查詢條件
// 在add方法中使用類的方法實現條件的設置
criteria.add(Restrictions.eq("id", 1));
criteria.add(Restrictions.eq("name", "CSDN"));
List<Company> list = criteria.list();
for (Company company : list) {
    System.out.println(company.getId() + "=" + company.getName());
}

  like

Criteria criteria = session.createCriteria(Company.class);
// 添加查詢條件
criteria.add(Restrictions.like("name", "%D%"));
List<Company> list = criteria.list();

Restrictions類的常用方法

方法名稱 描述
Restrictions.eq 等於 =
Restrictions.allEq 使用Map,使用key/value進行多個等於的判斷
Restrictions.gt 大於 >
Restrictions.ge 大於等於 >=
Restrictions.lt 小於 <
Restrictions.le 小於等於 <=
Restrictions.between 對應SQL的between子句
Restrictions.like 對應SQL的like子句
Restrictions.in 對應SQL的in子句
Restrictions.and and關係
Restrictions.or or關係
Restrictions.sqlRestriction SQL限定查詢

排序查詢

  調用addOrder設置排序規則,即對哪個屬性採用升序還是降序排序,如:

// 升序(默認)
/*Criteria criteria = session.createCriteria(Company.class);
criteria.addOrder(Order.asc("id"));*/
// 降序
Criteria criteria = session.createCriteria(Company.class);
criteria.addOrder(Order.desc("id"));
List<Company> list = criteria.list();

Order類的常用方法

方法名稱 描述
Order.asc 升序
Order.desc 降序

分頁查詢

Criteria criteria = session.createCriteria(Company.class);
// 設置分頁的開始位置
criteria.setFirstResult(0);
// 設置每頁的記錄數
criteria.setMaxResults(3);
List<Company> list = criteria.list();
for (Company company : list) {
    System.out.println(company.getId() + "=" + company.getName());
}

統計查詢

使用Projections工具類的方法進行統計和分組。

Criteria criteria = session.createCriteria(Company.class);
criteria.setProjection(Projections.rowCount());
Object obj = criteria.uniqueResult();
Long longObj = (Long) obj;
int count = longObj.intValue();
System.out.println(count);

Projections類的常用方法

方法名稱 描述
Projections.avg 求平均值
Projections.count 統計某屬性的數量
Projections.countDistinct 統計某屬性不同值的數量
Projections.groupProperty 指定某個屬性爲分組屬性
Projections.max 求最大值
Projections.min 求最小值
Projections.projectionList 創建一個ProjectionList對象
Projections.rowCount 查詢結果集中的記錄條數
Projections.sum 求某屬性的合計

離線查詢

  通常離線查詢可以在表示層建立,然後傳入業務層進行查詢。

DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Company.class);
// 最終執行時才需要到session對象
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Company> list = criteria.list();
for (Company company : list) {
    System.out.println(company.getId() + "=" + company.getName());
}

多表查詢

MySQL中的多表查詢

(1)內連接

/*內連接*/
SELECT * FROM t_company c, t_employee e WHERE c.id = e.cid

SELECT * FROM t_company c INNER JOIN t_employee e ON c.id = e.cid

(2)左外連接

/*左外連接*/
SELECT * FROM t_company c LEFT OUTER JOIN t_employee e ON c.id = e.cid

(3)右外連接

/*右外連接*/
SELECT * FROM t_company c RIGHT OUTER JOIN t_employee e ON c.id = e.cid

HQL中的多表查詢

內連接

  語法:from 實體類名稱 別名 inner join 別名.存儲另一個實體類對象的set屬性名稱。如:

Query query = session.createQuery("from Company c inner join c.employees");
List<Object[]> list = query.list();
for (Object[] objs: list) {
    Company company = (Company) objs[0];
    Employee employee = (Employee) objs[1];
    System.out.println(company.getId() + ": " + company.getName() + " " + employee.getId() + ": " + employee.getName());
}

  調用query對象的list方法後,方法返回的是一個List集合,集合中的每個部分是數組形式。

迫切內連接

  迫切內連接本質上也是內連接操作,不同的是使用內連接返回list中每部分是數組,而迫切內連接返回list中每部分是對象。
  語法:from 實體類名稱 別名 inner join fetch 別名.存儲另一個實體類對象的set屬性名稱。如:

Query query = session.createQuery("from Company c inner join fetch c.employees");
List<Company> list = query.list();
for (Company c : list) {
    System.out.println(c.getId() + ": " + c.getName());
}

左外連接

  語法:from 實體類名稱 別名 left outer join 別名.存儲另一個實體類對象的set屬性名稱。如:

Query query = session.createQuery("from Company c left outer join c.employees");
List<Object[]> list = query.list();
for (Object[] objs: list) {
    Company company = (Company) objs[0];
    if(objs[1] != null) {
        Employee employee = (Employee) objs[1];
        System.out.println(company.getId() + ": " + company.getName() + " " + employee.getId() + ": " + employee.getName());
    } else {
        System.out.println(company.getId() + ": " + company.getName() + " null : null");
    }
}

  調用query對象的list方法後,方法返回的是一個List集合,集合中的每個部分是數組形式。

迫切左外連接

  迫切左外連接本質上也是左外連接操作,不同的是使用左外連接返回list中每部分是數組,而迫切左外連接返回list中每部分是對象。
  語法:from 實體類名稱 別名 left outer join fetch 別名.存儲另一個實體類對象的set屬性名稱。如:

Query query = session.createQuery("from Company c left outer join fetch c.employees");
List<Company> list = query.list();
for (Company c : list) {
    System.out.println(c.getId() + ": " + c.getName());
}

右外連接

  語法:from 實體類名稱 別名 right outer join 別名.存儲另一個實體類對象的set屬性名稱。如:

Query query = session.createQuery("from Company c right outer join c.employees");
List<Object[]> list = query.list();
for (Object[] objs: list) {
    Employee employee = (Employee) objs[1];
    if(objs[0] != null) {
        Company company = (Company) objs[0];
        System.out.println(company.getId() + ": " + company.getName() + " " + employee.getId() + ": " + employee.getName());
    } else {
        System.out.println("null : null " + employee.getId() + ": " + employee.getName());
    }
}

注:沒有迫切右外連接!!!

Hibernate檢索策略

檢索策略的概念

  在Hibernate中檢索策略分爲兩類:
第一類 立即查詢:調用查詢方式將立即發送查詢語句到數據庫進行查詢,如根據ID調用get方法查詢,會立即發送查詢到數據庫。如:

// 獲取ID爲3的公司的所有員工
// 調用get方法將立即發送SQL語句查詢數據庫
Company company = session.get(Company.class, 3);
System.out.println(company.getId() + ": " + company.getName());

第二類 延遲查詢:調用方法時不會立即發送查詢語句到數據庫進行查詢,而是在真正需要到數據時才發送語句查詢數據庫,如根據ID調用load方法查詢,調用load方法不會馬上發送語句查詢數據庫。如:

// 獲取ID爲3的公司的所有員工
// 調用load方法之後,不會立即發送SQL語句到數據庫進行查詢
Company company = session.load(Company.class, 3);
// 返回對象ID時,不會發送SQL語句進行查詢
System.out.println(company.getId());
// 得到對象其他屬性值時才發送查詢語句
System.out.println(company.getName());

  注:在以上代碼測試過程中,發現沒有延遲查詢的效果,原因不明,待查明!!!
  延遲查詢又分爲兩類:
(1)類級別延遲:如根據ID查詢返回實體類對象,調用load方法不會立即發送語句到數據庫進行查詢。
(2)關聯級別延遲:如查詢某個公司的所有員工記錄的過程。查詢到某公司後得到公司的實體類對象,該對象擁有包含所有員工信息的set集合屬性,在未使用該屬性內的數據時,並不會發送語句到數據庫查詢員工信息。如:

// 根據ID查詢公司,這裏並沒有自動查詢該公司的所有員工
Company company = session.get(Company.class, 3);

// 下面這一句將發送查詢語句到數據庫進行查詢
Set<Employee> employees = company.getEmployees();

System.out.println("test");
System.out.println(employees.size());

關聯級別延遲操作

<set>集合上的fetch和lazy

  在映射文件中可以配置實現關聯級別延遲的方式。如在Company.hbm.xml中配置:
在set標籤上使用屬性fetch和lazy:

  • fetch:控制的是查詢其關聯對象的時候採用的SQL語句的格式。
    • select:默認值。發送一條select語句查詢其關聯對象。
    • join:發送一條迫切做外連接查詢關聯對象。
    • subselect:發送一條子查詢查詢其關聯對象。
  • lazy:控制的是查詢其關聯對象的時候是否採用延遲加載的策略。
    • true:默認值。默認查詢關聯對象的時候採用延遲加載。
    • false:查詢關聯對象的時候不採用延遲加載。
    • extra:極其懶惰,查詢關聯對象的時候,採用比延遲加載更懶惰的方式進行查詢。如需要得到記錄的總條數,其只會使用count聚合函數查詢記錄總數。

測試:配置Company.hbml映射文件的set標籤
(1)<set name="employees" fetch="select" lazy="true">

// 根據ID查詢公司,這裏並沒有自動查詢該公司的所有員工
Company company = session.get(Company.class, 3);

// 下面這一句將發送select查詢語句到數據庫進行查詢
Set<Employee> employees = company.getEmployees();

System.out.println("test");
System.out.println(employees.size());

(2)<set name="employees" fetch="select" lazy="false">

// 根據ID查詢公司時也查詢了該公司的所有員工信息
Company company = session.get(Company.class, 3);

Set<Employee> employees = company.getEmployees();

System.out.println("test");
System.out.println(employees.size());

(3)<set name="employees" fetch="select" lazy="extra">

// 根據ID查詢公司,這裏並沒有自動查詢該公司的所有員工
Company company = session.get(Company.class, 3);

// 只發送查詢記錄總數的語句到數據庫,如下:
Set<Employee> employees = company.getEmployees();

System.out.println("test");
System.out.println(employees.size());

  獲取員工記錄時,發送的SQL語句:

Hibernate: 
    select
        count(id) 
    from
        t_employee 
    where
        cid =?

批量抓取

  如,默認情況下,查詢所有公司下的所有員工信息。我們可以先查詢所有公司記錄再遍歷每個公司獲取公司下的所有員工信息。在關聯級別延遲加載下,只有公司去獲取員工信息時纔會發送查詢語句進行查詢。在這樣的情況下,Hibernate會根據公司記錄逐條發送查詢員工記錄的語句到數據庫,影響效率。如:

Criteria criteria = session.createCriteria(Company.class);
List<Company> list = criteria.list();
for (Company company : list) {
  // 每次遍歷才發送查詢當前公司下的所有員工記錄信息
    Set<Employee> employees = company.getEmployees();
    for (Employee employ : employees) {
        System.out.println(company.getId() + ": " + company.getName() + " " + employ.getId() + ": " + employ.getName());
    }
}

  通過配置公司Company.hbm.xml中set標籤的batch-size值,可以減少發送查詢語句的次數,批量查詢。如:

<set name="employees" batch-size="10">

  這時遍歷一次後,會發現類似如下查詢,使用in幫我們查詢了後續多條員工記錄信息,而不是一條一條的查詢:

Hibernate: 
    select
        employees0_.cid as cid5_1_1_,
        employees0_.id as id1_1_1_,
        employees0_.id as id1_1_0_,
        employees0_.name as name2_1_0_,
        employees0_.gender as gender3_1_0_,
        employees0_.phone as phone4_1_0_,
        employees0_.cid as cid5_1_0_ 
    from
        t_employee employees0_ 
    where
        employees0_.cid in (
            ?, ?, ?
        )
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章