本文包括:
1匠抗、Hibernate 的查詢方式
2嵌言、HQL (Hibernate Query Language) 查詢
3蝠检、HQL 的投影查詢
4怖亭、HQL 的聚合函數(shù)查詢
5涎显、QBC (Query By Criteria ) 條件查詢
6、QBC 離線條件查詢
7兴猩、SQL查詢方式(了解)
8期吓、HQL 多表查詢
9、延遲加載
10倾芝、Hibernate 的查詢策略
1讨勤、Hibernate 的查詢方式
-
根據(jù)唯一標(biāo)識 OID 的檢索方式
- session.get(XXX.class,OID)
可利用斷點(diǎn)調(diào)試觀察,執(zhí)行該行代碼時的控制臺輸出為:
select * from xx where id = ?
-
對象的導(dǎo)航的方式
- Customer.getLinkmans()
執(zhí)行該行代碼時晨另,控制臺輸出:
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) 是面向?qū)ο蟮牟樵冋Z言, 它和 SQL 查詢語言有些相似
在 Hibernate 提供的各種檢索方式中, HQL 是使用最廣的一種檢索方式
-
HQL 與 SQL 的關(guān)系:
HQL 查詢語句是面向?qū)ο蟮慕枘颍琀ibernate 負(fù)責(zé)解析 HQL 查詢語句, 然后根據(jù)對象-關(guān)系映射文件中的映射信息, 把 HQL 查詢語句翻譯成相應(yīng)的 SQL 語句.
HQL 查詢語句中的主體是域模型中的類及類的屬性
SQL 查詢語句是與關(guān)系數(shù)據(jù)庫綁定在一起的刨晴。SQL查詢語句中的主體是數(shù)據(jù)庫表及表的字段
-
HQL基本的查詢格式:
支持方法鏈的編程,即直接調(diào)用
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 框架提供了分頁的方法割捅,咱們可以調(diào)用方法來完成分頁
-
兩個方法如下
setFirstResult(a) -- 從哪條記錄開始奶躯,如果查詢是從第一條開啟,值是0
setMaxResults(b) -- 每頁查詢的記錄條數(shù)
-
演示代碼如下
List<LinkMan> list = session.createQuery("from LinkMan").setFirstResult(0).setMaxResults().list();
MySQL 對于分頁的實(shí)現(xiàn):
limit ?,? (注意:參數(shù)的值 從 0 開始)
第一個參數(shù):該頁從第幾個記錄開始亿驾,有公式:
(currentPage - 1) * pageSize
第二個參數(shù):每頁顯示多少個記錄嘹黔,一般取個固定值,如 10
-
帶條件的查詢:
Query.setString(0,"男");
Query.setLong(0,2L);
Query.setParameter("?號的位置莫瞬,默認(rèn)從0開始","參數(shù)的值"); -- 這個方法不用像上面兩個方法一樣考慮參數(shù)的具體類型
-
按名稱綁定參數(shù)的條件查詢(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();
對應(yīng) JDBC 的代碼:
PreparedStatement.setString(1,"男");
注意:JDBC 的查詢從1開始儡蔓!
3、HQL 的投影查詢
投影查詢就是想查詢某一字段的值或者某幾個字段的值
-
投影查詢的案例
-
如果查詢多個字段疼邀,返回的 list 中每個元素是對象數(shù)組(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)); }
-
如果查詢兩個字段喂江,也可以把這兩個字段封裝到對象中
-
先在持久化類中提供對應(yīng)字段的構(gòu)造方法
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; // 持久化類中一定要有無參的構(gòu)造方法,所以需要手動重載 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 的聚合函數(shù)查詢
聚合函數(shù):count(),avg(),max(),min(),sum()获询,返回的是一個數(shù)值
-
獲取總的記錄數(shù)
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 最省事蹬铺,得到后可以轉(zhuǎn)化為具體的子類尝哆,在上面的代碼中,調(diào)用了
Number.longValue()
甜攀,于是轉(zhuǎn)為了 Long 類型秋泄。 -
獲取某一列數(shù)據(jù)的和
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()
的方法來設(shè)置參數(shù)恒序,參數(shù)使用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); // 設(shè)置排序 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); // 設(shè)置排序 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 框架提供的工具類奸焙,使用該工具類來設(shè)置查詢條件)
條件查詢使用 Criteria 接口的 add 方法,用來傳入條件彤敛。
-
使用 Restrictions 的添加條件的方法,來添加條件了赌,例如:
Restrictions.eq -- 相等
Restrictions.gt -- 大于號
Restrictions.ge -- 大于等于
Restrictions.lt -- 小于
Restrictions.le -- 小于等于
Restrictions.between -- 在之間墨榄,閉區(qū)間
Restrictions.like -- 模糊查詢
Restrictions.in -- 范圍
Restrictions.and -- 并且
Restrictions.or -- 或者
- Restrictions.isNull -- 判斷某個字段是否為空
-
Restrictions.in 示例:
首先看參數(shù)類型:
Restrictions.in(String propertyName, Collection values)
發(fā)現(xiàn),應(yīng)該在第二個參數(shù)傳入 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 示例:
首先看參數(shù)類型袄秩,發(fā)現(xiàn)有兩種重載方法:
Restrictions.or(Criterion lhs, Criterion rhs) Restrictions.or(Criterion... predicates)
很明顯,第一個是兩個條件,第二個是可變參數(shù)之剧,所以條件數(shù)量不定郭卫,但是可以發(fā)現(xiàn),參數(shù)類型都是 Criterion 背稼,故都應(yīng)該傳入
Restrictions.XX()
贰军。Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); Criteria criteria = session.createCriteria(Linkman.class); // 設(shè)置排序 criteria.addOrder(Order.desc("lkm_id")); // 設(shè)置查詢條件 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;
-
聚合函數(shù)查詢(Projection 的聚合函數(shù)的接口,而 Projections 是 Hibernate 提供的工具類蟹肘,使用該工具類設(shè)置聚合函數(shù)查詢)
-
使用 QBC 的聚合函數(shù)查詢词疼,需要使用
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();
-
注意,如果想先執(zhí)行
select count(*) from xxx
帘腹,再執(zhí)行select * from xxx
贰盗,不能直接在后面執(zhí)行編寫 QBC 語句,應(yīng)該先“恢復(fù)” Projection// 創(chuàng)建QBC查詢接口 Criteria criteria = session.createCriteria(Linkman.class); // 設(shè)置聚合函數(shù)的方式 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); // 繼續(xù)查詢所有的聯(lián)系人 select * from 表 List<Linkman> mans = criteria.list();
-
6阳欲、QBC 離線條件查詢
-
離線條件查詢使用的是 DetachedCriteria 接口進(jìn)行查詢舵盈,離線條件查詢對象在創(chuàng)建的時候不需要使用 Session 對象,在添加條件 時也不需要 Session 對象球化,只有在查詢的時候使用 Session 對象即可书释,所以叫做離線條件查詢。
為什么要有離線條件查詢赊窥?
一般情況下爆惧,在業(yè)務(wù)層開啟 Session 后,在持久層對數(shù)據(jù)進(jìn)行操作锨能,而在 web 層需要接收條件查詢的若干條件扯再,所以在 web 層就設(shè)置條件會很方便,又因?yàn)?Criteria 需要由 Session 創(chuàng)建址遇,所以無法在 web 層設(shè)置條件熄阻,于是離線條件查詢出現(xiàn)了。
-
創(chuàng)建離線條件查詢對象
DetachedCriteria criteria = DetachedCriteria.forClass(Linkman.class);
-
具體的代碼如下倔约,注意順序秃殉,這樣是可行的
DetachedCriteria criteria = DetachedCriteria.forClass(Linkman.class); // 設(shè)置查詢條件 criteria.add(Restrictions.eq("lkm_gender", "男")); Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); // 查詢數(shù)據(jù) 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 語句進(jìn)行查詢钾军,HQL 語句和 SQL 語句的查詢語法比較類似
-
內(nèi)連接查詢
-
顯示內(nèi)連接
select * from customers c inner join orders o on c.cid = o.cno;
-
隱式內(nèi)連接
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 多表查詢時的兩種方式
-
迫切和非迫切:
非迫切返回結(jié)果是 Object[]
迫切連接返回的結(jié)果是對象,把客戶的信息封裝到客戶的對象中绢要,把訂單的信息封裝到客戶的 Set 集合中
-
非迫切內(nèi)連接使用 inner join 吏恭,默認(rèn)返回的是 Object 數(shù)組
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();
-
迫切內(nèi)連接使用 inner join fetch ,返回的是實(shí)體對象
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 轉(zhuǎn)為 Set 集合重罪,可以消除重復(fù)的數(shù)據(jù)
-
-
左外連接查詢
非迫切左外連接:封裝成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樱哼、延遲加載
原理:先獲取到代理對象哀九,當(dāng)真正使用到該對象中的屬性的時候,才會發(fā)送 SQL 語句搅幅,是 Hibernate 框架提升性能的方式
-
類級別的延遲加載
-
Session 對象的 load 方法默認(rèn)就是延遲加載阅束,例如:
Customer c = session.load(Customer.class, 1L);
斷點(diǎn)調(diào)試,發(fā)現(xiàn)茄唐,執(zhí)行該行代碼時息裸,控制臺輸出為空,即沒有發(fā)送 SQL 語句琢融,再執(zhí)行下面這行代碼:
System.out.println(c.getCust_name);
此時發(fā)現(xiàn)界牡,控制臺輸出對應(yīng)的 SQL 語句。
Session.get(Class,id) 沒有延遲加載
-
使類級別的延遲加載失效(一般情況下使用默認(rèn)的延遲加載)
-
在映射配置文件的
<class>
標(biāo)簽上配置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標(biāo)簽name屬性:表示集合的名稱 --> <set name="linkmans" inverse="true"> <!-- 需要出現(xiàn)子標(biāo)簽 --> <!-- 外鍵的字段 --> <key column="lkm_cust_id"/> <one-to-many class="com.itheima.domain.Linkman"/> </set> </class> </hibernate-mapping>
Hibernate.initialize(Object proxy);
-
-
-
關(guān)聯(lián)級別的延遲加載(查詢某個客戶漾抬,當(dāng)查看該客戶下的所有聯(lián)系人是是否是延遲加載)
-
代碼:
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();
最后在控制臺上發(fā)現(xiàn)宿亡,在執(zhí)行第二個輸出語句時,控制臺才會輸出如下類似語句纳令,所以關(guān)聯(lián)級別的延遲加載也是默認(rèn)的
select * from Linkmans where cust_id = ?
-
10挽荠、Hibernate 的查詢策略
查詢策略:使用 Hibernate 查詢一個對象的關(guān)聯(lián)對象時,應(yīng)該如何查詢平绩,這是 Hibernate 的一種優(yōu)化手段!!!
-
Hibernate 查詢策略要解決的問題:
-
查詢的時機(jī)
Customer c1 = (Customer) session.get(Customer.class, 1); System.out.println(c1.getLinkmans().size());
lazy 屬性解決查詢的時機(jī)的問題圈匆,配置是否該持久化類是否采用延遲加載
-
查詢的語句形式
List<Customer> list = session.createQuery("from Customer").list(); for(Customer c : list){ System.out.println(c.getLinkmans()); }
fetch 屬性就可以解決查詢語句的形式的問題,具體見下文
-
在 <set>
標(biāo)簽上配置策略
-
在
<set>
標(biāo)簽上使用 fetch 和 lazy 屬性-
fetch 的取值 -- 控制 SQL 語句生成的格式
select -- 默認(rèn)值捏雌,發(fā)送普通的查詢語句
join -- 連接查詢跃赚,發(fā)送的是一條迫切左外連接,配置了join性湿,lazy 無論取哪種值都是同一個效果
subselect -- 子查詢纬傲,發(fā)送一條子查詢查詢其關(guān)聯(lián)對象(需要使用
list()
方法進(jìn)行測試)
-
lazy 的取值 -- 查找關(guān)聯(lián)對象的時候是否采用延遲
true -- 默認(rèn)值,延遲加載
false -- 不延遲
extra -- 極其懶惰
-
<set>
標(biāo)簽上的默認(rèn)值是fetch="select"
和lazy="true"
在 <many-to-one>
標(biāo)簽上配置策略
-
在<many-to-one>標(biāo)簽上使用fetch和lazy屬性
-
fetch 的取值 -- 控制SQL的格式
select -- 默認(rèn)值肤频,發(fā)送基本的查詢查詢
join -- 發(fā)送迫切左外連接查詢
-
lazy 的取值 -- 控制加載關(guān)聯(lián)對象是否采用延遲
false -- 不采用延遲加載.
-
proxy -- 默認(rèn)值叹括,代理,現(xiàn)在是否采用延遲加載宵荒,由另一端(即一對多的一方)的
<class>
上的 lazy 確定- 如果一方的
<class>
上的lazy="true"
汁雷,proxy 的值就是 true (延遲加載)
- 如果一方的
<class>
上的lazy="false"
,proxy的值就是 false (不采用延遲.)
- 如果一方的
-
在
<many-to-one>
標(biāo)簽上的默認(rèn)值是fetch="select"
和lazy="proxy"