1. 程式人生 > >hibernate中的inverse屬性用法

hibernate中的inverse屬性用法

1、inverse屬性只會在集合屬性裡面出現,一對多,多對多

2、inverse屬性應該配置在一的一端,有助於提高效率。即inverse=false;

4. hibernate如何根據pojo來更新資料庫

4.0 在commit/flush之前,hibernate不會對pojo物件作神祕的處理。
4.0.1 在select查詢出pojo時,hibernate根據“欄位--屬性”的對應關係,用欄位的值填充pojo的屬性;
然後根據“關係標記”生成sql語句從relationTable中查詢出滿足條件的relationPojo,並把這些relatinPojo
放到“關係屬性”中。這個過程是機械的。

4.0.2 在pojo物件被查出來後,到commit(或flush)之前,它將是一個普通的java物件,hibernate不會做額外的手腳。
比如,不會限制你設定一個屬性的值為null或其它任何值
在集合類Set的add(object)操作時, 不會改變object的值,不會檢查引數object是否是一個pojo物件
設定mainPojo的一個“橋屬性”的值,不會自動設定relationPojo的對應的“橋屬性”的值。
執行session.delete(pojo)時,pojo本身沒有變化,他的屬性值也沒有變化。
執行session.save(pojo)時,如果pojo的id不是hibernate或資料庫生成,則它的值沒有變化。
如果pojo的id是hibernate或資料庫生成,則hibernate會把id給pojo設上去。

extend: 對lazy=true的set,hibernate在進行set的操作(呼叫java.util.Set中宣告的方法)時
會先inialize這個set,僅此而已。而inialize僅僅是從資料庫中撈出set的資料。
如果一個set已經被inialize了,那麼對它進行的操作就是java.util.Set介面中定義的語義。

另外,如果id由hibernate來生成,那麼在save(pojo)時,hibernate會改變該pojo,會設定它的id,這
可能改變該pojo的hashCode,詳細地討論見帖《》

mapping檔案中標記的某些屬性及pojo物件的操作會對資料庫操作產生影響,這些影響都是在commit時才會起作用。
而在commit前pojo的狀態不受它們的影響。

不過,待commit之時,將由hibernate完全掌控,它好像知道pojo物件從建立到commit這中間的所有變化。


4.01. 關聯更新
"關係標記"對應的屬性是一個pojo或一個pojo的集合,修改“關係屬性”的值能會導致更新mainTable表,也可能會更新relationTable表。

這種更新暫叫“關聯更新”。


4.1.inverse屬性的作用(假定沒有設定cascade屬性)
4.1.1 “只有集合標記(set/map/list/array/bag)才有inverse屬性”。
————不妨以標記set為例,具體為“一個地區(Address表)的學校(School表)” -- address.schoolSet。

4.1.2 “set的inverse屬性決定是否把對set的改動反映到資料庫中去。
inverse=false————反映;inverse=true————不反映”
inverse屬性預設為false

對<one-to-many>和<many-to-many>子標記,這兩條都適用。
不管是對set做什麼操作,4.1.2都適用。

4.1.3當inverse=false時,hibernate如何將對set的改動反映到資料庫中:

對set的操作主要有:(1)新增元素 address.getSchoolSet().add(oneSchool);
(2)刪除元素 address.getSchoolSet().remove(oneSchool);
(3)刪除set address.setSchoolSet(null);
(4)設新set address.setSchoolSet( newSchoolSet);
(5)轉移set otherSchoolSet = otherAddress.getSchoolSet();
otherAddress.setSchoolSet(null);
address.setSchoolSet(otherSchoolSet);
(6)改變set中元素的屬性的值 如果是改變key屬性,這會導致異常
如果改變的是普通的屬性,則hibernate認為set沒有變化(在後面可以看出緣由)。
所以這種情形不予考慮。

改變set後,hibernate對資料庫的操作根據是<one-to-many>關係還是<many-to-many>關係而有不同。

對one-to-many,對school set的改動,會改變表SCHOOL中的資料:
#SCHOOL_ID是school表的主鍵,SCHOOL_ADDRESS是school表中的位址列位
#表School的外來鍵為SCHOOL_ADDRESS,它對應表Address的主鍵ADDRESS_ID
(11)insert oneSchool———— sqlInsertRowString:
update SCHOOL set SCHOOL_ADDRESS=? where SCHOOL_ID=?
(僅僅update foreign-key的值。)
(22)delete oneSchool———— sqlDeleteRowString:
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ID=?
(很奇怪,把foreign-key設定為null不知道有什麼實際意義?)
(33)delete 屬於某一address的所有school ————sqlDeleteString:
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ADDRESS=?
(44)update ————sqlUpdateRowString:"", no need

對many-to-many,對school set的改動,會改變關係表ADDRESS_SCHOOL中的資料:
#“地區————學校”的關係為多對多的關係有點牽強,只是為了方便與上面的one-to-many作比較
#假設有一個關係表ADDRESS_SCHOOL,有兩個欄位ADDRESS_ID, SCHOOL_ID,
#這兩個欄位分別對應ADDRESS和SCHOOL兩表的key
(11)insert的SQL語句為: insert into ADDRESS_SCHOOL(ADDRESS_ID, SCHOOL_ID)
values(?,?)
(22)delete的SQL語句為: delete from ADDRESS_SCHOOL
where ADDRESS_ID=? AND SCHOOL_ID=?
(33)delete all的SQL語句為: delete from ADDRESS_SCHOOL
where ADDRESS_ID=?
(44)update的sql語句為 ————sqlUpdateRowString:
update ADDRESS_SCHOOL set ADDRESS_ID=?
where ADDRESS_ID=? AND SCHOOL_ID=?

對set的操作(1),hibernate會執行(11)sqlInsertRowString
對set的操作(2),hibernate會執行(22)sqlDeleteRowString
對set的操作(3),hibernate會執行(33)sqlDeleteString
對set的操作(4),老的schoolSet因為沒有所屬的address,所以被全部delete掉,即先執行(33)sqlDeleteString
然後新增新的schoolSet,即再執行sqlInsertRowString
對set的操作(5),實際上就是將set從一個pojo轉移到另一pojo:
首先,執行sqlDeleteString,刪除掉otherAddress所屬的school
然後,執行sqlDeleteString,刪除掉address原先的school
最後,執行sqlInsertRowString,將otherSchoolSet新增給address

總結:(1)對one-to-many而言,改變set,會讓hibernate執行一系列的update語句, 不會delete/insert資料
(2)對many-to-many而言,改變set,只修改關係表的資料,不會影響many-to-many的另一方。
(3)雖然one-to-many和many-to-many的資料庫操作不一樣,但目的都是一個:維護資料的一致性。執行的sql都
只涉及到“橋欄位”,不會考慮或改變其他的欄位,所以對set的操作(6)是沒有效果地。
extend:對list,可能還會維護index欄位。

4.1.4 “inverse與cascade沒有什麼關係,互無牽扯。”
commit後,這兩個屬性發揮作用的時機不同,hibernate會根據對pojo物件的改動,及cascade屬性的設定,
生成一系列的Action,比如UpdateAction,DeleteAction,InsertAction等,每個Action都有execute方法以執行對應的sql語句。
待所有這些Action都生成好了後,hibernate再一起執行它們,在執行sql前,inverse屬性起作用,
當inverse=true時,不執行sql;當inverse=false時,執行sql。

4.1.5 inverse的預設值為false,所以inverse屬性預設會進行“關聯更新”。

4.1.6 建議:只對set + many-to-many設定inverse=false,其他的標記不考慮inverse屬性。
糟糕的是,不設定inverse屬性時,inverse預設為false。

4.2. 級聯(cascade)屬性的作用:
4.2.1 只有“關係標記”才有cascade屬性:many-to-one,one-to-one ,any,
set(map, bag, idbag, list, array) + one-to-many(many-to-many)

4.2.2 級聯指的是當主控方執行操作時,關聯物件(被動方)是否同步執行同一操作。
pojo和它的關係屬性的關係就是“主控方 -- 被動方”的關係,如果關係屬性是一個set,那麼被動方就是set中的一個一個元素,。
比如:學校(School)有三個屬性:地區(Address),校長(TheMaster)和學生(Set, 元素為Student)
執行session.delete(school)時,級聯決定是否執行session.delete(Address),session.delete(theMaster),
是否對每個aStudent執行session.delete(aStudent)。

extend:這點和inverse屬性是有區別的。見4.3.

4.2.3 一個操作因級聯cascade可能觸發多個關聯操作。前一個操作叫“主控操作”,後一個操作叫“關聯操作”。
cascade屬性的可選值:
all : 所有情況下均進行關聯操作。
none:所有情況下均不進行關聯操作。這是預設值。
save-update:在執行save/update/saveOrUpdate時進行關聯操作。
delete:在執行delete時進行關聯操作。

具體執行什麼“關聯操作”是根據“主控操作”來的:
“主控操作” “關聯操作”
session.saveOrUpdate --> session.saveOrUpdate (執行saveOrUpdate實際上會執行save或者update)
session.save ----> session.saveOrUpdate
session.udpate --> session.saveOrUpdate
session.delete --> session.delete

4.2.4 主控操作和關聯操作的先後順序是“先儲存one,再儲存many;先刪除many,再刪除one;先update主控方,再update被動方”
對於one-to-one,當其屬性constrained="false"(預設值)時,它可看作one-to-many關係;
當其屬性constrained="true"時,它可看作many-to-one關係;
對many-to-many,它可看作one-to-many。

比如:學校(School)有三個屬性:地區(Address),校長(TheMaster,其constrained="false")和學生(Set, 元素為Student)
當執行session.save(school)時,
實際的執行順序為:session.save(Address);
session.save(school);
session.save(theMaster);
for( 對每一個student ){
session.save(aStudent);
}

當執行session.delete(school)時,
實際的執行順序為:session.delete(theMaster);
for( 對每一個student ){
session.delete(aStudent);
}
session.delete(school);
session.delete(Address);

當執行session.update(school)時,
實際的執行順序為:session.update(school);
session.saveOrUpdate(Address);
session.saveOrUpdate(theMaster);
for( 對每一個student ){
session.saveOrUpdate(aStudent);
}
注意:update操作因級聯引發的關聯操作為saveOrUpdate操作,而不是update操作。
saveOrUpdate與update的區別是:前者根據操作物件是儲存了還是沒有儲存,而決定執行update還是save

extends: 實際中,刪除學校不會刪除地區,即地區的cascade一般設為false
另外,many-to-many關係很少設定cascade=true,而是設定inverse=false。這個反映了cascade和inverse的區別。見4.3

4.2.6 cascade的預設值為false,所以inverse屬性預設會進行“關聯更新”。

4.2.7 總結:級聯(cascade)就是操作一個物件時,對它的屬性(其cascade=true)也進行這個操作。


4.3 inverse和cascade的比較
這兩個屬性本身互不影響,但起的作用有些類似,都能引發對關係表的更新。

4.3.1 inverse只對set+one-to-many(或many-to-many)有效,對many-to-one, one-to-one無效。
cascade對關係標記都有效。

4.3.2 inverse對集合物件整體起作用,cascade對集合物件中的一個一個元素起作用,如果集合為空,那麼cascade不會引發關聯操作。
比如將集合物件置為null, school.setStudentSet(null)
inverse導致hibernate執行:udpate STUDENT set SCHOOL_ID=null where SCHOOL_ID=?
cascade則不會執行對STUDENT表的關聯更新, 因為集合中沒有元素。

再比新增一個school, session.save(school)
inverse導致hibernate執行:
for( 對(school的每一個student ){
udpate STUDENT set SCHOOL_ID=? where STUDENT_ID=? //將學生的school_id改為新的school的id
}
cascade導致hibernate執行:
for( 對school的每一個student ){
session.save(aStudent); //對學生執行save操作
}

extends:如果改變集合中的部分元素(比如新增一個元素),
inverse: hibernate先判斷哪些元素改變了,對改變的元素執行相應的sql
cascade: 它總是對集合中的每個元素執行關聯操作。
(在關聯操作中,hibernate會判斷操作的物件是否改變)

4.3.2 兩個起作用的時機不同:
cascade:在對主控方操作時,級聯發生。
inverse: 在flush時(commit會自動執行flush),對session中的所有set,hibernate判斷每個set是否有變化,
對有變化的set執行相應的sql,執行之前,會有個判斷:if( inverse == true ) return;

可以看出cascade在先,inverse在後。

4.3.3 inverse 對set + one-to-many 和 set + many-to-many 起的作用不同。hibernate生成的sql不同。
對one-to-many,hibernate對many方的資料庫表執行update語句。
對many-to-many, hibernate對關係表執行insert/update/delte語句,注意不是對many方的資料庫表而是關係表。

cascase 對set都是一致的,不管one-to-many還是many-to-many。都簡單地把操作傳遞到set中的每個元素。所以它總是更新many
方的資料庫表。

4.3.4 建議:只對set + many-to-many設定inverse=false,其他的標記不考慮inverse屬性,都設為inverse=true。

對cascade,一般對many-to-one,many-to-many,constrained=true的one-to-one 不設定級聯刪除