本文包括:
1、Hibernate 的查詢方式
2、HQL (Hibernate Query Language) 查詢
3、HQL 的投影查詢
4、HQL 的聚合函數查詢
5、QBC (Query By Criteria ) 條件查詢
6、QBC 離線條件查詢
7、SQL查詢方式(了解)
8、HQL 多表查詢
9、延遲加載
10、Hibernate 的查詢策略
1、Hibernate 的查詢方式
-
根據唯一標識 OID 的檢索方式
- session.get(XXX.class,OID)
可利用斷點調試觀察,執行該行代碼時的控制臺輸出為:
select * from xx where id = ?
-
對象的導航的方式
- Customer.getLinkmans()
執行該行代碼時,控制臺輸出:
select * from cst_linkmans where cust_id = ?
-
HQL 的檢索方式
- Hibernate Query Language -- Hibernate 的查詢語言
-
QBC 的檢索方式
- Query By Criteria -- 條件查詢
-
SQL 檢索方式(了解)
- 本地的 SQL 檢索 -- 較麻煩,在 Hibernate 中不推薦使用
2、HQL (Hibernate Query Language) 查詢
-
HQL 的介紹:
HQL(Hibernate Query Language) 是面向對象的查詢語言, 它和 SQL 查詢語言有些相似
在 Hibernate 提供的各種檢索方式中, HQL 是使用最廣的一種檢索方式
-
HQL 與 SQL 的關系:
HQL 查詢語句是面向對象的,Hibernate 負責解析 HQL 查詢語句, 然后根據對象-關系映射文件中的映射信息, 把 HQL 查詢語句翻譯成相應的 SQL 語句.
HQL 查詢語句中的主體是域模型中的類及類的屬性
SQL 查詢語句是與關系數據庫綁定在一起的。SQL查詢語句中的主體是數據庫表及表的字段
-
HQL基本的查詢格式:
支持方法鏈的編程,即直接調用
list()
方法-
簡單的代碼如下
session.createQuery("from Customer").list();
-
使用別名的方式:
-
可以使用別名的方式
session.createQuery("from Customer c").list(); session.createQuery("select c from Customer c").list();
-
-
排序查詢:
-
排序查詢和SQL語句中的排序的語法是一樣的
-
升序
session.createQuery("from Customer order by cust_id").list();
-
降序
session.createQuery("from Customer order by cust_id desc").list();
-
-
-
分頁查詢(不用管 DBMS 是 Oracle 還是 MySQL,Hibernate 自動幫我們完成分頁):
Hibernate 框架提供了分頁的方法,咱們可以調用方法來完成分頁
-
兩個方法如下
setFirstResult(a) -- 從哪條記錄開始,如果查詢是從第一條開啟,值是0
setMaxResults(b) -- 每頁查詢的記錄條數
-
演示代碼如下
List<LinkMan> list = session.createQuery("from LinkMan").setFirstResult(0).setMaxResults().list();
MySQL 對于分頁的實現:
limit ?,? (注意:參數的值 從 0 開始)
第一個參數:該頁從第幾個記錄開始,有公式:
(currentPage - 1) * pageSize
第二個參數:每頁顯示多少個記錄,一般取個固定值,如 10
-
帶條件的查詢:
Query.setString(0,"男");
Query.setLong(0,2L);
Query.setParameter("?號的位置,默認從0開始","參數的值"); -- 這個方法不用像上面兩個方法一樣考慮參數的具體類型
-
按名稱綁定參數的條件查詢(HQL語句中的 ? 號換成 :名稱 的方式)
Query query = session.createQuery("from Customer where name = :aaa and age = :bbb"); query.setString("aaa", "李健"); query.setInteger("bbb", 38); List<Customer> list = query.list();
對應 JDBC 的代碼:
PreparedStatement.setString(1,"男");
注意:JDBC 的查詢從1開始!
3、HQL 的投影查詢
投影查詢就是想查詢某一字段的值或者某幾個字段的值
-
投影查詢的案例
-
如果查詢多個字段,返回的 list 中每個元素是對象數組(object[])
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)); }
-
如果查詢兩個字段,也可以把這兩個字段封裝到對象中
-
先在持久化類中提供對應字段的構造方法
public class Customer { private Long cust_id; private String cust_name; private Long cust_user_id; private Long cust_create_id; private String cust_source; private String cust_industry; private String cust_level; private String cust_linkman; private String cust_phone; private String cust_mobile; // 持久化類中一定要有無參的構造方法,所以需要手動重載 public Customer(){}; public Customer(cust_name,cust_level){}; }
-
然后使用下面這種 HQL 語句的方式:
select new Customer(c.cust_name,c.cust_level) from Customer c
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); }
-
-
4、HQL 的聚合函數查詢
聚合函數:count(),avg(),max(),min(),sum(),返回的是一個數值
-
獲取總的記錄數
Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); List<Number> list = session.createQuery("select count(c) from Customer c").list(); Long count = list.get(0).longValue(); System.out.println(count); tr.commit();
Number 類是 Integer、Float、Double、Long 等等的父類,所以泛型寫 Number 最省事,得到后可以轉化為具體的子類,在上面的代碼中,調用了
Number.longValue()
,于是轉為了 Long 類型。 -
獲取某一列數據的和
Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); List<Number> list = session.createQuery("select sum(c.cust_id) from Customer c").list(); Long count = list.get(0).longValue(); System.out.println(count); tr.commit();
5、QBC (Query By Criteria ) 條件查詢
-
簡單查詢,使用的是 Criteria 接口
List<Customer> list = session.createCriteria(Customer.class).list(); for (Customer customer : list) { System.out.println(customer); }
-
排序查詢
-
需要使用
addOrder()
的方法來設置參數,參數使用org.hibernate.criterion.Order 對象的 asc() or desc() 方法
如:
criteria.addOrder(Order.desc("lkm_id"));
-
具體代碼如下:
Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); Criteria criteria = session.createCriteria(Linkman.class); // 設置排序 criteria.addOrder(Order.desc("lkm_id")); List<Linkman> list = criteria.list(); for (Linkman linkman : list) { System.out.println(linkman); } tr.commit();
-
-
分頁查詢
-
QBC 的分頁查詢也是使用兩個方法,與 HQL 一樣
setFirstResult();
setMaxResults();
-
代碼如下;
Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); Criteria criteria = session.createCriteria(Linkman.class); // 設置排序 criteria.addOrder(Order.desc("lkm_id")); criteria.setFirstResult(0); criteria.setMaxResults(3); List<Linkman> list = criteria.list(); for (Linkman linkman : list) { System.out.println(linkman); } tr.commit();
-
-
條件查詢(Criterion 是查詢條件的接口,Restrictions 類是 Hibernate 框架提供的工具類,使用該工具類來設置查詢條件)
條件查詢使用 Criteria 接口的 add 方法,用來傳入條件。
-
使用 Restrictions 的添加條件的方法,來添加條件,例如:
Restrictions.eq -- 相等
Restrictions.gt -- 大于號
Restrictions.ge -- 大于等于
Restrictions.lt -- 小于
Restrictions.le -- 小于等于
Restrictions.between -- 在之間,閉區間
Restrictions.like -- 模糊查詢
Restrictions.in -- 范圍
Restrictions.and -- 并且
Restrictions.or -- 或者
- Restrictions.isNull -- 判斷某個字段是否為空
-
Restrictions.in 示例:
首先看參數類型:
Restrictions.in(String propertyName, Collection values)
發現,應該在第二個參數傳入 Collection ,于是可以這樣演示:
Criteria criteria = session.createCriteria(Linkman.class); List<Long> params = new ArrayList<Long>(); params.add(1L); params.add(2L); params.add(7L); // 使用in 方法查詢 criteria.add(Restrictions.in("lkm_id", params));
SQL:
select * from cst_linkman where lkm_id in (1,2,7);
-
Restrictions.or 示例:
首先看參數類型,發現有兩種重載方法:
Restrictions.or(Criterion lhs, Criterion rhs) Restrictions.or(Criterion... predicates)
很明顯,第一個是兩個條件,第二個是可變參數,所以條件數量不定,但是可以發現,參數類型都是 Criterion ,故都應該傳入
Restrictions.XX()
。Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); Criteria criteria = session.createCriteria(Linkman.class); // 設置排序 criteria.addOrder(Order.desc("lkm_id")); // 設置查詢條件 criteria.add(Restrictions.or(Restrictions.eq("lkm_gender", "男"), Restrictions.gt("lkm_id", 3L))); List<Linkman> list = criteria.list(); for (Linkman linkman : list) { System.out.println(linkman); } tr.commit();
SQL:
select * from Linkman where lkm_gender='男' or lkm_id=3 order by lkm_id desc;
-
聚合函數查詢(Projection 的聚合函數的接口,而 Projections 是 Hibernate 提供的工具類,使用該工具類設置聚合函數查詢)
-
使用 QBC 的聚合函數查詢,需要使用
criteria.setProjection()
方法如:
criteria.setProjection(Projections.rowCount());
又如:
criteria.setProjection(Projections.count("lkm_id"));
-
具體的代碼如下:
Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); Criteria criteria = session.createCriteria(Linkman.class); criteria.setProjection(Projections.rowCount()); List<Number> list = criteria.list(); Long count = list.get(0).longValue(); System.out.println(count); tr.commit();
-
注意,如果想先執行
select count(*) from xxx
,再執行select * from xxx
,不能直接在后面執行編寫 QBC 語句,應該先“恢復” Projection// 創建QBC查詢接口 Criteria criteria = session.createCriteria(Linkman.class); // 設置聚合函數的方式 select count(lkm_id) from 表; 5 criteria.setProjection(Projections.count("lkm_id")); List<Number> list = criteria.list(); Long count = list.get(0).longValue(); System.out.println(count); criteria.setProjection(null); // 繼續查詢所有的聯系人 select * from 表 List<Linkman> mans = criteria.list();
-
6、QBC 離線條件查詢
-
離線條件查詢使用的是 DetachedCriteria 接口進行查詢,離線條件查詢對象在創建的時候不需要使用 Session 對象,在添加條件 時也不需要 Session 對象,只有在查詢的時候使用 Session 對象即可,所以叫做離線條件查詢。
為什么要有離線條件查詢?
一般情況下,在業務層開啟 Session 后,在持久層對數據進行操作,而在 web 層需要接收條件查詢的若干條件,所以在 web 層就設置條件會很方便,又因為 Criteria 需要由 Session 創建,所以無法在 web 層設置條件,于是離線條件查詢出現了。
-
創建離線條件查詢對象
DetachedCriteria criteria = DetachedCriteria.forClass(Linkman.class);
-
具體的代碼如下,注意順序,這樣是可行的
DetachedCriteria criteria = DetachedCriteria.forClass(Linkman.class); // 設置查詢條件 criteria.add(Restrictions.eq("lkm_gender", "男")); Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); // 查詢數據 List<Linkman> list = criteria.getExecutableCriteria(session).list(); for (Linkman linkman : list) { System.out.println(linkman); } tr.commit();
7、SQL查詢方式(了解)
-
基本語法
Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); SQLQuery sqlQuery = session.createSQLQuery("select * from cst_linkman where lkm_gender = ?"); sqlQuery.setParameter(0,"男"); sqlQuery.addEntity(Linkman.class); List<Linkman> list = sqlQuery.list(); System.out.println(list); tr.commit();
8、HQL 多表查詢
-
多表查詢使用 HQL 語句進行查詢,HQL 語句和 SQL 語句的查詢語法比較類似
-
內連接查詢
-
顯示內連接
select * from customers c inner join orders o on c.cid = o.cno;
-
隱式內連接
select * from customers c,orders o where c.cid = o.cno;
-
-
外連接查詢
-
左外連接
select * from customers c left join orders o on c.cid = o.cno;
-
右外連接
select * from customers c right join orders o on c.cid = o.cno;
-
-
-
HQL 多表查詢時的兩種方式
-
迫切和非迫切:
非迫切返回結果是 Object[]
迫切連接返回的結果是對象,把客戶的信息封裝到客戶的對象中,把訂單的信息封裝到客戶的 Set 集合中
-
非迫切內連接使用 inner join ,默認返回的是 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)); } tr.commit();
-
迫切內連接使用 inner join fetch ,返回的是實體對象
Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); 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); } tr.commit();
把 List 轉為 Set 集合,可以消除重復的數據
-
-
左外連接查詢
非迫切左外連接:封裝成List<Object[]>
-
迫切左外連接:
Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); List<Customer> list = session.createQuery("from Customer c left join fetch c.linkmans").list(); Set<Customer> set = new HashSet<Customer>(list); for (Customer customer : set) { System.out.println(customer); } tr.commit();
9、延遲加載
原理:先獲取到代理對象,當真正使用到該對象中的屬性的時候,才會發送 SQL 語句,是 Hibernate 框架提升性能的方式
-
類級別的延遲加載
-
Session 對象的 load 方法默認就是延遲加載,例如:
Customer c = session.load(Customer.class, 1L);
斷點調試,發現,執行該行代碼時,控制臺輸出為空,即沒有發送 SQL 語句,再執行下面這行代碼:
System.out.println(c.getCust_name);
此時發現,控制臺輸出對應的 SQL 語句。
Session.get(Class,id) 沒有延遲加載
-
使類級別的延遲加載失效(一般情況下使用默認的延遲加載)
-
在映射配置文件的
<class>
標簽上配置lazy="false"
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.itheima.domain.Customer" table="cst_customer" lazy="false"> <id name="cust_id" column="cust_id"> <generator class="native"/> </id> <property name="cust_name" column="cust_name"/> <property name="cust_user_id" column="cust_user_id"/> <property name="cust_create_id" column="cust_create_id"/> <property name="cust_source" column="cust_source"/> <property name="cust_industry" column="cust_industry"/> <property name="cust_level" column="cust_level"/> <property name="cust_linkman" column="cust_linkman"/> <property name="cust_phone" column="cust_phone"/> <property name="cust_mobile" column="cust_mobile"/> <!-- 配置一方 --> <!-- set標簽name屬性:表示集合的名稱 --> <set name="linkmans" inverse="true"> <!-- 需要出現子標簽 --> <!-- 外鍵的字段 --> <key column="lkm_cust_id"/> <one-to-many class="com.itheima.domain.Linkman"/> </set> </class> </hibernate-mapping>
Hibernate.initialize(Object proxy);
-
-
-
關聯級別的延遲加載(查詢某個客戶,當查看該客戶下的所有聯系人是是否是延遲加載)
-
代碼:
Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); Customer c = session.get(Customer.class, 1L); System.out.println("============="); System.out.println(c.getLinkmans().size()); tr.commit();
最后在控制臺上發現,在執行第二個輸出語句時,控制臺才會輸出如下類似語句,所以關聯級別的延遲加載也是默認的
select * from Linkmans where cust_id = ?
-
10、Hibernate 的查詢策略
查詢策略:使用 Hibernate 查詢一個對象的關聯對象時,應該如何查詢,這是 Hibernate 的一種優化手段!!!
-
Hibernate 查詢策略要解決的問題:
-
查詢的時機
Customer c1 = (Customer) session.get(Customer.class, 1); System.out.println(c1.getLinkmans().size());
lazy 屬性解決查詢的時機的問題,配置是否該持久化類是否采用延遲加載
-
查詢的語句形式
List<Customer> list = session.createQuery("from Customer").list(); for(Customer c : list){ System.out.println(c.getLinkmans()); }
fetch 屬性就可以解決查詢語句的形式的問題,具體見下文
-
在 <set>
標簽上配置策略
-
在
<set>
標簽上使用 fetch 和 lazy 屬性-
fetch 的取值 -- 控制 SQL 語句生成的格式
select -- 默認值,發送普通的查詢語句
join -- 連接查詢,發送的是一條迫切左外連接,配置了join,lazy 無論取哪種值都是同一個效果
subselect -- 子查詢,發送一條子查詢查詢其關聯對象(需要使用
list()
方法進行測試)
-
lazy 的取值 -- 查找關聯對象的時候是否采用延遲
true -- 默認值,延遲加載
false -- 不延遲
extra -- 極其懶惰
-
<set>
標簽上的默認值是fetch="select"
和lazy="true"
在 <many-to-one>
標簽上配置策略
-
在<many-to-one>標簽上使用fetch和lazy屬性
-
fetch 的取值 -- 控制SQL的格式
select -- 默認值,發送基本的查詢查詢
join -- 發送迫切左外連接查詢
-
lazy 的取值 -- 控制加載關聯對象是否采用延遲
false -- 不采用延遲加載.
-
proxy -- 默認值,代理,現在是否采用延遲加載,由另一端(即一對多的一方)的
<class>
上的 lazy 確定- 如果一方的
<class>
上的lazy="true"
,proxy 的值就是 true (延遲加載)
- 如果一方的
<class>
上的lazy="false"
,proxy的值就是 false (不采用延遲.)
- 如果一方的
-
在
<many-to-one>
標簽上的默認值是fetch="select"
和lazy="proxy"