1. 程式人生 > >hibernate的中的查詢與級聯操作

hibernate的中的查詢與級聯操作

導致 pan 出了 場景 效果 自動保存 情況下 主鍵 只需要

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來維護從表的外鍵值,這樣會降低程序的性能。

hibernate的中的查詢與級聯操作