1. 程式人生 > >hibernate 學習筆記2

hibernate 學習筆記2

1.Criteria查詢介面適用於組合多個限制條件來搜尋一個查詢集。

要使用Criteria,需要遵循以下步驟:

  *建立查詢介面: Criteria criteria=session.createCriteria(User.class);

  *設定查詢條件: criteria.add(Restrictions.gt(“age”,10);

  *查詢資料:      List<User> list=criteria.list();

 

2.關係對映:一個使用者(cust_customer)對應多個聯絡人(cust_linkman)

*在CustCustomer.hbm.xml中配置

<!--設定與多方的聯絡-->

        <!--

             name:javabean中set集合的名稱

             key:column:外來鍵名稱

             one-to-many class:set集合中類的全路徑-->

        <set name="linkMans">

            <!--外來鍵-->

            <key column="lkm_cust_id"/>

            <!--對應關係-->

            <one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman"/>

        </set>

 

*在CustLinkman.hbm.xml中配置

<!--設定一對多-->

        <!--

            name:javabean的屬性名

            class:屬性的類的全名

            column:外來鍵名-->

        <many-to-one name="customer" class="edu.whu.swe.lxl.learn.hibernate.model.CustCustomer" column="lkm_cust_id"/>

 

3.雙向關聯:既儲存主表,也儲存從表

*java程式碼:

    public void testBathDirectSave(){

//        新建一個使用者和多個聯絡人

        CustCustomer customer=new CustCustomer();

        customer.setCustName("馬蓉");

        CstLinkman lkm1 = new CstLinkman();

        lkm1.setLkmName("強哥");

 

        CstLinkman lkm2 = new CstLinkman();

        lkm2.setLkmName("小宋");

 

//        雙向關聯

//        客戶關聯聯絡人

        customer.getLinkMans().add(lkm1);

        customer.getLinkMans().add(lkm2);

//        聯絡人關聯客戶

        lkm1.setCustomer(customer);

        lkm2.setCustomer(customer);

        session.save(customer);

        session.save(lkm1);

        session.save(lkm2);

}

 

*SQL輸出:

**Hibernate:

    insert

    into

        cust_customer

        (cust_name, cust_user_id, cust_create_id, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile)

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?)

**Hibernate:

    insert

    into

        cst_linkman

        (lkm_name, lkm_gender, lkm_phone, lkm_mobile, lkm_email, lkm_qq, lkm_position, lkm_memo, lkm_cust_id)

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?)

**Hibernate:

    insert

    into

        cst_linkman

        (lkm_name, lkm_gender, lkm_phone, lkm_mobile, lkm_email, lkm_qq, lkm_position, lkm_memo, lkm_cust_id)

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?)

**Hibernate:

    update

        cst_linkman

    set

        lkm_cust_id=?

    where

        lkm_id=?

**Hibernate:

    update

        cst_linkman

    set

        lkm_cust_id=?

    where

        lkm_id=?

可以看到,雙向儲存時,對從表其實是做了更新操作的。

 

4.級聯儲存:級聯儲存只需要儲存一方關係,多方關係會自動儲存。先測試只儲存主表:

*配置:通過在一方關係的hbm.xml中配置set的cascade屬性為save-update,從而使得多方關係中的Javabean從Transient狀態自動轉為Persistent態。

<set name="linkMans" cascade="save-update">

            <!--外來鍵-->

            <key column="lkm_cust_id"/>

            <!--對應關係-->

            <one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman"/>

        </set>

*java程式碼:

public void testSingleDirectSave(){

//        新建一個使用者和多個聯絡人

        CustCustomer customer=new CustCustomer();

        customer.setCustName("馬蓉1");

        CstLinkman lkm1 = new CstLinkman();

        lkm1.setLkmName("強哥1");

 

        CstLinkman lkm2 = new CstLinkman();

        lkm2.setLkmName("小宋1");

 

//        單向關聯

//        只需要客戶關聯聯絡人

        customer.getLinkMans().add(lkm1);

        customer.getLinkMans().add(lkm2);

//        不需要聯絡人關聯客戶

 

//        只需要儲存客戶,聯絡人由框架自動儲存

        session.save(customer);

 

    }

 

*SQL操作:

**Hibernate:

    insert

    into

        cust_customer

        (cust_name, cust_user_id, cust_create_id, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile)

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?)

**Hibernate:

    insert

    into

        cst_linkman

        (lkm_name, lkm_gender, lkm_phone, lkm_mobile, lkm_email, lkm_qq, lkm_position, lkm_memo, lkm_cust_id)

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?)

**Hibernate:

    insert

    into

        cst_linkman

        (lkm_name, lkm_gender, lkm_phone, lkm_mobile, lkm_email, lkm_qq, lkm_position, lkm_memo, lkm_cust_id)

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?)

**Hibernate:

    update

        cst_linkman

    set

        lkm_cust_id=?

    where

        lkm_id=?

**Hibernate:

    update

        cst_linkman

    set

        lkm_cust_id=?

    where

        lkm_id=?

 

這種方法雖然java程式碼中只需要單項儲存就可以了,但是仍然先對多方從表進行insert操作後在進行update操作,一共有5次SQL操作。

5.級聯儲存,將級聯儲存配置在從表,在java中也只進行從表的儲存操作。這種方法可以減少SQL語句。

*配置,同樣,也需要在多端的hbm.xml中配置cascade屬性為save-update:
<many-to-one name="customer" class="edu.whu.swe.lxl.learn.hibernate.model.CustCustomer" column="lkm_cust_id" cascade="save-update"/>

*Java程式碼:

public void testSingleDirectInManySave(){

//        新建一個使用者和多個聯絡人

        CustCustomer customer=new CustCustomer();

        customer.setCustName("馬蓉2");

        CstLinkman lkm1 = new CstLinkman();

        lkm1.setLkmName("強哥2");

 

        CstLinkman lkm2 = new CstLinkman();

        lkm2.setLkmName("小宋2");

 

//        單向關聯

//        這是隻關聯聯絡人,而不對客戶方進行操作

        lkm1.setCustomer(customer);

        lkm2.setCustomer(customer);

//        不需要聯絡人關聯客戶

 

//        只儲存聯絡人,客戶由框架自動儲存

        session.save(lkm1);

        session.save(lkm2);

 

    }

*SQL語句:

**Hibernate:

    insert

    into

        cust_customer

        (cust_name, cust_user_id, cust_create_id, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile)

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?)

**Hibernate:

    insert

    into

        cst_linkman

        (lkm_name, lkm_gender, lkm_phone, lkm_mobile, lkm_email, lkm_qq, lkm_position, lkm_memo, lkm_cust_id)

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?)

**Hibernate:

    insert

    into

        cst_linkman

        (lkm_name, lkm_gender, lkm_phone, lkm_mobile, lkm_email, lkm_qq, lkm_position, lkm_memo, lkm_cust_id)

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?)

 

可以看到,這種在多端執行級聯儲存的方式,沒有進行update操作,而是一步到位只進行insert操作,減少資料庫的讀寫。

6.cascade設定有以下幾種:

 1)all: 包含出了delete-orphan外的所有情況,即save-update和delete。

 2)none: 所有情況下均不進行關聯操作。這是預設值。

 3)save-update: 在執行save/update/saveOrUpdate時進行關聯操作。

 4)delete: 在執行delete 時進行關聯操作。

  5)delete-orphan: 孤兒刪除,只能配置在一端。在delete的基礎之上,當需要把外來鍵設定為null時,直接刪除外來鍵對應的多端,一般用以對remove的支援。

 6)all-delete-orphan: 當一個節點在物件圖中成為孤兒節點時,刪除該節點。

我們使用得是save-update,當執行級聯儲存操作時,如果相關聯的物件在表中沒有記錄,則會一起save,即執行insert SQL操作,如果有(說明在快照區有相關聯的物件的副本),則看是否發生改變,在覺得是否update相關聯的物件。

所謂孤兒,只有在一對多的情況中才存在,指的是一端已經沒有了,多端還有一些存在,它的外來鍵是null。

7. inverse設定是否讓主表來update從表的值來,且只對save和update有效。

這個是我比較難理解的一個點。

inverse的值是boolean值,也就是能設定為true或false。 如果一方的對映檔案中設定為true,說明在對映關係(一對多,多對多等)中讓從表自己來維護外來鍵值。如果為false,就主表來設定從表的外來鍵值。預設值是false,也就是會去update從表的外來鍵值。 並且這屬性只能在一端設定(inverse是set的屬性)。比如一對多,這個一端。也就是在有set集合的這方設定。

維護關係:維護什麼關係呢?包括兩個方面

1、也就是維護外來鍵的關係了,通俗點講,就是哪一方去設定這個被外來鍵約束的欄位的值。就拿上面這個例子來說,cust_customer和cst_linkman兩張表不管進行什麼操作,只要關係到了另一張表,就不可避免的要操作外來鍵欄位,比如,linkman查詢自己所對應的客戶,就得通過被外來鍵約束的欄位值到customer中的主鍵中查詢,如果customer想查詢自己有哪些聯絡人,就得拿著自己的主鍵值跟linkman中的外來鍵欄位做比較,找到相同的值則是屬於自己的聯絡人。 這個是查詢操作, 現在如果是新增操作呢,linkman表中新增一條記錄,並且對應於customer表中的一個客戶,linkman中有被外來鍵約束脩飾的欄位,那是通過CstLinkman(java)的insert語句就對這個外來鍵欄位賦值,還是讓CustCustomer(java)物件使用update語句對其賦值呢,兩個都能對這個外來鍵欄位的值進行操作,誰去操作呢?如果不做設定,兩個都會操作,就出現了上面說的這種有5次SQL操作的情況(對於linkman表,有兩次insert是有CstLinkman發出的,還有兩次update是有CustCustomer發出的)。雖然不會出現問題,但是會影響效能。其實只需要CstLinkman發出的兩次insert就可以插入兩條聯絡人記錄了。如果讓對方維護外來鍵關係,則自己這方就不維護了。

2、維護級聯的關係,就是cascade的那幾種設定了。

如果在一端的CustCustomer.hbm.xml中加入set的inverse=”true”屬性,這時候無論在java中執行哪方的儲存,都不會出現多餘的update SQL操作,說明一端(主表)不會去維護外來鍵關係,而只有讓從表去維護。再次強調,inverse不影響是否級聯操作,只是設定誰來維護外來鍵的值。很多文章都誤以為是設定cascade是否有效,這是錯誤的,誤導人。

但是,inverse不是亂設定的,inverse如果設定為true,那麼一端是不會去維護外來鍵的值的,它會留給多端去維護,但如果多端沒有通過setCuster()方法來設定相應的外來鍵值,則為java類屬性預設值Null,此時外來鍵的值就會為null。所以一定要記得為從表的物件也設定好外來鍵值。

由此可以看出,hibernate的級聯儲存方式,指的是如何傳送SQL語句,而不會對java物件本身的賦值行為執行任何的操作,對任何java物件的賦值,都需要程式設計師自己去執行。

Hibernate唯一能自動對java物件賦值的操作是查詢操作。

參考文獻:https://www.cnblogs.com/whgk/p/6135591.html

         

8.在級聯刪除中,應把cascade=delete級聯配置在一端,如果配置在多端,可能會導致多端資料刪除不趕緊,留下孤點資料,原因如下:

當delete配置在多端時,則由多端去維護delete的一致性,所以當刪除多端的某一條資料時,級聯刪除通過外來鍵值查詢到一端表的主鍵,找到對應的記錄,並把這個記錄刪除。刪除一端的資料之後,資料庫如果設定的外來鍵約束是 on delete set null,則留下了孤點資料。

而且,根據一般的業務邏輯,也應該是刪除了一端資料之後,才刪除所有的多端記錄。而刪除某一個多端記錄時,是不需要級聯刪除一端的資料的。舉個例子:建築-房間是一對多關係,刪除了多端的房間,是不需要整棟建築的。但是建築如果刪除了,房間自然就不存在了,所有的房間都應該被刪除。所以delete應該配置在一端。

9.delete和delete-orphan的區別,stackoverflow上已經有人說的很明白了:

Cascade DELETE means if this entity is deleted, delete the related entity or entities.

DELETE_ORPHAN means if an entity is removed from a related one-to-many collection, then not only disassociate it from the current entity, but delete it.

To give you an example, consider two entities: House and Room.

DELETE on the Room list on House means that if you delete the House then delete all it's Rooms.

DELETE_ORPHAN on the Room list on House means if you remove a Room from that collection, delete it entirely. Without it, the Room would still exist but not be attached to anything (hence "orphan").

In UML and OO modelling terms, this is basically the difference between composition and aggregation. The House->Room relationship is an example of composition. A Room is part of a House and doesn't exist independently.

An example of aggregation is, say, Class (parent) to Student (child). Delete the Class and the Student still exists (undoubtedly in other classes). Removing the Student from the Class doesn't typically mean deleting him or her.

大致的意思就是,delete只在刪除一端的記錄時,會刪除多端的所有記錄;而delete-orphan在當處於持久態的一端物件對他的set執行了remove時,會刪除所remove的多端物件對應的從表記錄。

在UML中,delete和delete-orphan的使用場景分別是“聚合”和“組成”。學生-班級關係是“聚合”,因此學生允許孤兒(沒有班級),所以使用delete比較合理。建築-房間關係是“組成”,如果一棟建築沒有了某個房間,那麼這棟房間就不存在了,也就說說房間是不可能是脫離建築的孤兒,所以當一個房間從建築中remove時,就應該把房間從從表中刪除,所以使用delete-orphan比較合適。

值得注意的是,delete-orphan包括了delete的效果。

參考:https://stackoverflow.com/questions/1377585/what-is-the-difference-between-delete-orphan-and-delete

 

總結:級聯儲存通常配置在多端(從表),級聯刪除配置在一端(主表)delete-orphan或delete。這樣做的好處是:

1)當儲存資料時,只需要設定多端(從表)的外來鍵屬性值並儲存多端,主表會通過級聯儲存自動儲存,因為主表無需維護外來鍵,因此可以無需設定主表的set值,讓它為null就行。

2)當刪除時,通過刪除主表來刪除一端記錄,並且級聯刪除所有的關聯的多端(從表)記錄。

3)刪除多端(從表)的某一記錄之後,主表不受影響,可以防止因主表級聯刪除之後,引起從表中其他記錄的外來鍵為null,甚至因no action或restrict而導致無法級聯刪除主表記錄(與預期不一致)。

4)如果要通過一端(主表)來級聯儲存從表,也可以在一端(主表)中配置級聯儲存,並把reverse=true設定開啟,此時,雖然不用手動session.save()多端物件(從表記錄),但是需要在java程式碼中手動設定從表的外來鍵值,否則外來鍵的值為null。另外,在一端執行remove之後,也不會對多端執行delete操作(因為一端不去維護外來鍵了);如果要讓一端(主表)來維護從表的外來鍵值,則reverse=false(預設就是false),這樣在級聯儲存從表記錄(inset)之後,還會發出update SQL來維護從表的外來鍵值,這樣會降低程式的效能。