hibernate 學習筆記3
1. 多對多關聯:
在雙方都要用一個型別為Set的屬性儲存對方的資訊,並在對映配置檔案中指定這個屬性的名字,並指定中間表。還需要通過<key column=””>來指定自己在中間表中對應的外來鍵。在<many-to-many>標籤中,要配置對方的類,並且指定對方類在中間表中的對應的外來鍵
*學生表配置檔案:
<class name="edu.whu.swe.lxl.learn.hibernate.model.stucourse.Student" table="student" schema="dbforlearn">
<id name="id" column="id" >
<generator class="native"/>
</id>
<property name="num" column="num"/>
<property name="name" column="name"/>
<!--配置set集合描述類Student中的courses屬性和中間表資訊-->
<set name="courses" table="stu_course" >
<!--設定本類在連線表中對應的外來鍵-->
<key column="student"/>
<!--配置關聯類以及關聯類在中間表中的外來鍵-->
<many-to-many class="edu.whu.swe.lxl.learn.hibernate.model.stucourse.Course"
column="course"/>
</set>
</class>
*課程表的對映檔案:
<class name="edu.whu.swe.lxl.learn.hibernate.model.stucourse.Course" table="course" schema="dbforlearn">
<id name="id" column="id">
<generator class="native"
</id>
<property name="num" column="num"/>
<property name="name" column="name"/>
<!--配置關聯類在本類中對應的屬性以及中間表-->
<set name="students" table="stu_course">
<!--設定本類在連線表中對應的外來鍵-->
<key column="course"/>
<!--配置關聯類以及關聯類在中間表中對應的外來鍵-->
<many-to-many class="edu.whu.swe.lxl.learn.hibernate.model.stucourse.Student"
column="student"/>
</set>
</class>
*一般在多對多關係中,不存在級聯刪除的需求。在多對多中,千萬不要加入cascade=delete,否則會是災難性的。
2.查詢
1)唯一標識OID的檢索方式(重要)
Session.get(obj.class,OID)
2) 物件的導航方式(重要)
一對多查詢時,利用裡面包含的set
3)HQL的檢索方式(重要)
Hibernate Query Language ——hibernate的查詢語言
4)QBC的檢索方式(重要)
Query By Criteria ——條件查詢
5)SQL查詢
3.HQL查詢
* HQL是面向java物件的,和SQL有相似之處
*是hibernate中使用最廣泛的檢索方式
*hibernate負責解析HQL,然後根據ORM配置的對映關係,生成相應的SQL語句
*HQL操作的是類以及屬性,而不是資料表和欄位
4.延遲載入技術——在class標籤上配置
1)延遲載入先獲取到代理物件,當真正使用到該物件中的屬性且快取裡沒有值時,才會傳送SQL語句,是hibernate框架提升效能的主要方式。
2)類級別的延遲載入
*Session物件的load方法預設就是延遲載入
*Customer c=session.load(Customer.class,1L),沒有傳送SQL語句,當使用該物件的屬性時,才傳送SQL語句
【例項】
/**
<class name="edu.whu.swe.lxl.learn.hibernate.model.Customer" table="customer" >
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" column="name"/>
<property name="address" column="address"/>
<property name="age" column="age"/>
<property name="registerDate" column="register_date"/>
</class> */
public void testLoad(){
Customer customer = session.load(Customer.class, 3);
log.trace(customer.getId().toString());//沒有傳送SQL,因為ID已經由程式碼提供了,此時開始變成持久態
log.trace("+++++++++++++++++");
log.trace(customer.getName());//傳送SQL語句,因為此時快取裡沒有name資訊,必須要傳送SQL從資料庫中查詢
}
*使類級別的延遲載入失效:
**在<class>標籤上配置lazy=”false”
**Hibernate.initialize(Object proxy)
【例項】
/**
<class name="edu.whu.swe.lxl.learn.hibernate.model.Customer" table="customer" lazy="false">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" column="name"/>
<property name="address" column="address"/>
<property name="age" column="age"/>
<property name="registerDate" column="register_date"/>
</class>
*/
@Test
public void testLoadNotLazy() {
Customer customer = session.load(Customer.class, 3);
log.trace(customer.getId().toString());//沒有傳送SQL,因為ID已經由程式碼提供了,此時開始變成持久態
log.trace("+++++++++++++++++");
log.trace(customer.getName());//傳送SQL語句,因為此時快取裡沒有name資訊,必須要傳送SQL從資料庫中查詢
}
3)關聯級別的延遲載入。
* CustCustomer customer=session.get(CustCustomer.class,1L)不是延遲載入,但是它不會把代理類CustCustomer的關聯類CstLinkman載入進來,當使用CstLinkman linkmans=customer.getLinkmans()獲取關聯類物件的時候,因為沒有用到CstLinkman的任何屬性,也不需要知道有幾個linkman,所以還是不會載入。直到使用了linkmans裡的屬性時,才會傳送SQL載入關聯類物件。
【例項】
/**
* <set name="linkMans" cascade="delete-orphan" inverse="true">
<!--外來鍵-->
<key column="lkm_cust_id"/>
<!--對應關係-->
<one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman" />
</set>
*/
CustCustomer customer = session.get(CustCustomer.class, 19L);//直接傳送SQL查詢CustCustomer,但不查詢CstLinkman
log.trace(customer.getCustId().toString());//
log.trace("++++++++++++++++++++++++++");
Set linkmans=customer.getLinkMans();//還是不傳送SQL,因為還沒有必要查詢CstLinkman的屬性
log.trace("++++++++++++++++++++++++++");
log.trace(""+linkmans.size());//這個時候傳送SQL,因為不傳送,就不知道有幾個linkman了。
5.延遲載入技術——在set標籤上配置策略
1)在<set>標籤上使用fetch和lazy屬性
*fetch的取值 ——控制SQL語句的生成格式
** select ——預設值,傳送查詢語句
**join ——連線查詢,傳送的是一條左外連結查詢,此時,關聯表延遲載入失效(因為不需要再發起一次SQL查詢關聯表了,而是在一次外連結查詢中完成了所有表的載入)
**subselect ——子查詢,傳送一條子查詢完成關聯物件的查詢。(需要使用list()方法進行測試,如Criteria.list()或者Query.list())
*lazy的取值 ——查詢關聯物件的時候是否採用延遲載入
**true ——預設,延遲
**false ——不延遲
**extra ——極其懶惰,根據需要查詢,而不是查詢所有欄位,比如用一寫聚合函式,或者只查詢某一個欄位
一般都採用預設值即可
【示例】
*fetch=”join”
public void testJoinSet(){
/**
<set name="linkMans" cascade="delete-orphan" inverse="true" fetch="join>
<!--外來鍵-->
<key column="lkm_cust_id"/>
<!--對應關係-->
<one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman" />
</set>
*/
CustCustomer customer = session.get(CustCustomer.class, 19L);//直接傳送join SQL 左外連線查詢,一次性獲取所有的資料
log.trace(customer.getCustId().toString());
log.trace("++++++++++++++++++++++++++");
}
*fetch=”select” lazy=”extra”
@Test
public void testExtraLazy() {
/**
<set name="linkMans" cascade="delete-orphan" inverse="true" fetch="select" lazy="extra">
<!--外來鍵-->
<key column="lkm_cust_id"/>
<!--對應關係-->
<one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman" />
</set>
*/
CustCustomer customer = session.get(CustCustomer.class, 19L);//直接傳送SQL查詢CustCustomer,但不查詢CstLinkman
log.trace(customer.getCustId().toString());//
log.trace("++++++++++++++++++++++++++");
Set linkmans = customer.getLinkMans();//還是不傳送SQL,因為還沒有必要查詢CstLinkman的屬性
log.trace("++++++++++++++++++++++++++");
log.trace("" + linkmans.size());//傳送select count(lkm_id) form cst_linkman where lkm_cust_id=?,而不是查詢所有欄位
}
*fatch=”subselect”
public void testSubselectSet() {
/**
<set name="linkMans" cascade="delete-orphan" inverse="true" fetch="subselect">
<!--外來鍵-->
<key column="lkm_cust_id"/>
<!--對應關係-->
<one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman" />
</set>
*/
Query query = session.createQuery("from CustCustomer ");
List<CustCustomer> customers = query.list();//傳送SQL查詢CustCustomer,但不查詢CstLinkman
log.trace("++++++++++++++++++++++++++++++++++");
for(CustCustomer customer:customers){
Set linkmans
= customer.getLinkMans();//不傳送SQL
log.trace("++++++++++++++++++++++++++++++++++");
log.trace(""+linkmans.size());//在第一次迴圈時候,就傳送子查詢把List所有中所有使用者的所有聯絡人都一次性查詢出來,下次迴圈就不用傳送SQL了。如果不適用subselect,那麼迴圈一次,就傳送一次SQL查詢該客戶的聯絡人
}
}
6.優化技術——在<many-to-one>標籤中配置策略
1)在<many-to-one>標籤上使用fetch和lazy屬性
*fetch的取值 ——控制SQL語句的格式
**select ——預設,傳送基本的select語句查詢
**join ——無效,所以fetch只能是select,所以不用設定fetch
*lazy的取值
**false ——不採用延遲載入
**proxy ——預設值,是否延遲載入取決於一方set 標籤的lazy取值,多方和一方的lazy值一樣。
7.當fetch=select,lazy=true的時候,使用list時,對從表的查詢是一次迴圈獲取進行一次的,此時如果不想設定subselect進行子查詢,那麼可以設定set標籤的batch-size=10,從而達到批量獲取從表資料的目的。當設定batch-size=10時,hibernate會一次就取10個客戶的聯絡人。具體工作原理這樣的:
1)先獲取所有的客戶
2)再用select xxx xxx xxx from cst_linkman where cst_linkman.lkm_cust_id in( 10個客戶的id )批量獲取10個客戶的聯絡人。
總結:session.load採取的是類級別的延遲載入,並且在class標籤上配置當前類的延遲載入策略;session.get採取的是連線上的延遲載入,並且在set標籤上配置載入策略,在many-to-one上也可以進行配置。
8.延遲載入策略的優缺點及適用範圍:
1)有點
有應用程式決定要載入哪些物件,可以避免執行多餘的SQL語句,避免載入不程式不會訪問的物件,提高效能和空間利用率。
2)缺點
應用程式如果試圖訪問處於遊離態的代理類例項,必須保證它在持久化狀態是已經被初始化了。而延遲載入策略往往導致代理類例項相應的set沒有值,從而需要把遊離態物件通過session.update方法變成持久態,再去訪問set中的物件從而載入物件。
3)適用範圍
——一對多或者多對多
——應用程式不需要立即訪問或者根本不需要訪問的物件。
9.在配置檔案中配置的載入策略影響全域性,但是應用程式的需求可能是多樣的,在不同的時候需要不同的載入策略,因此,hibernate提供了在應用程式中指定載入策略的方式,應用程式中指定的載入方式將覆蓋配置檔案中的載入方式(區域性優先於整體原則)
10.一對一的載入預設是使用左外連線查詢。