?4. hibernate如何根據(jù)pojo來更新數(shù)據(jù)庫
4.0 在commit/flush之前,hibernate不會對pojo對象作神秘的處理。 4.0.1 在select查詢出pojo時,hibernate根據(jù)“字段--屬性”的對應關系,用字段的值填充pojo的屬性; 然后根據(jù)“關系標記”生成sql語句從relationTable中查詢出滿足條件的relationPojo,并把這些relatinPojo 放到“關系屬性”中。這個過程是機械的。 4.0.2 在pojo對象被查出來后,到commit(或flush)之前,它將是一個普通的java對象,hibernate不會做額外的手腳。 比如,不會限制你設置一個屬性的值為null或其它任何值 在集合類Set的add(object)操作時, 不會改變object的值,不會檢查參數(shù)object是否是一個pojo對象 設置mainPojo的一個“橋屬性”的值,不會自動設置relationPojo的對應的“橋屬性”的值。 執(zhí)行session.delete(pojo)時,pojo本身沒有變化,他的屬性值也沒有變化。 執(zhí)行session.save(pojo)時,如果pojo的id不是hibernate或數(shù)據(jù)庫生成,則它的值沒有變化。 如果pojo的id是hibernate或數(shù)據(jù)庫生成,則hibernate會把id給pojo設上去。 extend: 對lazy=true的set,hibernate在進行set的操作(調用java.util.Set中聲明的方法)時 會先inialize這個set,僅此而已。而inialize僅僅是從數(shù)據(jù)庫中撈出set的數(shù)據(jù)。 如果一個set已經被inialize了,那么對它進行的操作就是java.util.Set接口中定義的語義。 另外,如果id由hibernate來生成,那么在save(pojo)時,hibernate會改變該pojo,會設置它的id,這 可能改變該pojo的hashCode,詳細地討論見帖《》 mapping文件中標記的某些屬性及pojo對象的操作會對數(shù)據(jù)庫操作產生影響,這些影響都是在commit時才會起作用。 而在commit前pojo的狀態(tài)不受它們的影響。 不過,待commit之時,將由hibernate完全掌控,它好像知道pojo對象從創(chuàng)建到commit這中間的所有變化。 4.01. 關聯(lián)更新 "關系標記"對應的屬性是一個pojo或一個pojo的集合,修改“關系屬性”的值能會導致更新mainTable表,也可能會更新relationTable表。 這種更新暫叫“關聯(lián)更新”。 4.1.inverse屬性的作用(假定沒有設置cascade屬性) 4.1.1 “只有集合標記(set/map/list/array/bag)才有inverse屬性”。 ————不妨以標記set為例,具體為“一個地區(qū)(Address表)的學校(School表)” -- address.schoolSet。 4.1.2 “set的inverse屬性決定是否把對set的改動反映到數(shù)據(jù)庫中去。 inverse=false————反映;inverse=true————不反映” inverse屬性默認為false 對<one-to-many>和<many-to-many>子標記,這兩條都適用。 不管是對set做什么操作,4.1.2都適用。 4.1.3當inverse=false時,hibernate如何將對set的改動反映到數(shù)據(jù)庫中: 對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對數(shù)據(jù)庫的操作根據(jù)是<one-to-many>關系還是<many-to-many>關系而有不同。 對one-to-many,對school set的改動,會改變表SCHOOL中的數(shù)據(jù): #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中的數(shù)據(jù): #“地區(qū)————學校”的關系為多對多的關系有點牽強,只是為了方便與上面的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會執(zhí)行(11)sqlInsertRowString 對set的操作(2),hibernate會執(zhí)行(22)sqlDeleteRowString 對set的操作(3),hibernate會執(zhí)行(33)sqlDeleteString 對set的操作(4),老的schoolSet因為沒有所屬的address,所以被全部delete掉,即先執(zhí)行(33)sqlDeleteString 然后新增新的schoolSet,即再執(zhí)行sqlInsertRowString 對set的操作(5),實際上就是將set從一個pojo轉移到另一pojo: 首先,執(zhí)行sqlDeleteString,刪除掉otherAddress所屬的school 然后,執(zhí)行sqlDeleteString,刪除掉address原先的school 最后,執(zhí)行sqlInsertRowString,將otherSchoolSet新增給address 總結:(1)對one-to-many而言,改變set,會讓hibernate執(zhí)行一系列的update語句, 不會delete/insert數(shù)據(jù) (2)對many-to-many而言,改變set,只修改關系表的數(shù)據(jù),不會影響many-to-many的另一方。 (3)雖然one-to-many和many-to-many的數(shù)據(jù)庫操作不一樣,但目的都是一個:維護數(shù)據(jù)的一致性。執(zhí)行的sql都 只涉及到“橋字段”,不會考慮或改變其他的字段,所以對set的操作(6)是沒有效果地。 extend:對list,可能還會維護index字段。 4.1.4 “inverse與cascade沒有什么關系,互無牽扯。” commit后,這兩個屬性發(fā)揮作用的時機不同,hibernate會根據(jù)對pojo對象的改動,及cascade屬性的設置, 生成一系列的Action,比如UpdateAction,DeleteAction,InsertAction等,每個Action都有execute方法以執(zhí)行對應的sql語句。 待所有這些Action都生成好了后,hibernate再一起執(zhí)行它們,在執(zhí)行sql前,inverse屬性起作用, 當inverse=true時,不執(zhí)行sql;當inverse=false時,執(zhí)行sql。 4.1.5 inverse的默認值為false,所以inverse屬性默認會進行“關聯(lián)更新”。 4.1.6 建議:只對set + many-to-many設置inverse=false,其他的標記不考慮inverse屬性。 糟糕的是,不設置inverse屬性時,inverse默認為false。 4.2. 級聯(lián)(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 級聯(lián)指的是當主控方執(zhí)行操作時,關聯(lián)對象(被動方)是否同步執(zhí)行同一操作。 pojo和它的關系屬性的關系就是“主控方 -- 被動方”的關系,如果關系屬性是一個set,那么被動方就是set中的一個一個元素,。 比如:學校(School)有三個屬性:地區(qū)(Address),校長(TheMaster)和學生(Set, 元素為Student) 執(zhí)行session.delete(school)時,級聯(lián)決定是否執(zhí)行session.delete(Address),session.delete(theMaster), 是否對每個aStudent執(zhí)行session.delete(aStudent)。 extend:這點和inverse屬性是有區(qū)別的。見4.3. 4.2.3 一個操作因級聯(lián)cascade可能觸發(fā)多個關聯(lián)操作。前一個操作叫“主控操作”,后一個操作叫“關聯(lián)操作”。 cascade屬性的可選值: all : 所有情況下均進行關聯(lián)操作。 none:所有情況下均不進行關聯(lián)操作。這是默認值。 save-update:在執(zhí)行save/update/saveOrUpdate時進行關聯(lián)操作。 delete:在執(zhí)行delete時進行關聯(lián)操作。 具體執(zhí)行什么“關聯(lián)操作”是根據(jù)“主控操作”來的: “主控操作” “關聯(lián)操作” session.saveOrUpdate --> session.saveOrUpdate (執(zhí)行saveOrUpdate實際上會執(zhí)行save或者update) session.save ----> session.saveOrUpdate session.udpate --> session.saveOrUpdate session.delete --> session.delete 4.2.4 主控操作和關聯(lián)操作的先后順序是“先保存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)有三個屬性:地區(qū)(Address),校長(TheMaster,其constrained="false")和學生(Set, 元素為Student) 當執(zhí)行session.save(school)時, 實際的執(zhí)行順序為:session.save(Address); session.save(school); session.save(theMaster); for( 對每一個student ){ session.save(aStudent); } 當執(zhí)行session.delete(school)時, 實際的執(zhí)行順序為:session.delete(theMaster); for( 對每一個student ){ session.delete(aStudent); } session.delete(school); session.delete(Address); 當執(zhí)行session.update(school)時, 實際的執(zhí)行順序為:session.update(school); session.saveOrUpdate(Address); session.saveOrUpdate(theMaster); for( 對每一個student ){ session.saveOrUpdate(aStudent); } 注意:update操作因級聯(lián)引發(fā)的關聯(lián)操作為saveOrUpdate操作,而不是update操作。 saveOrUpdate與update的區(qū)別是:前者根據(jù)操作對象是保存了還是沒有保存,而決定執(zhí)行update還是save extends: 實際中,刪除學校不會刪除地區(qū),即地區(qū)的cascade一般設為false 另外,many-to-many關系很少設置cascade=true,而是設置inverse=false。這個反映了cascade和inverse的區(qū)別。見4.3 4.2.6 cascade的默認值為false,所以inverse屬性默認會進行“關聯(lián)更新”。 4.2.7 總結:級聯(lián)(cascade)就是操作一個對象時,對它的屬性(其cascade=true)也進行這個操作。 4.3 inverse和cascade的比較 這兩個屬性本身互不影響,但起的作用有些類似,都能引發(fā)對關系表的更新。 4.3.1 inverse只對set+one-to-many(或many-to-many)有效,對many-to-one, one-to-one無效。 cascade對關系標記都有效。 4.3.2 inverse對集合對象整體起作用,cascade對集合對象中的一個一個元素起作用,如果集合為空,那么cascade不會引發(fā)關聯(lián)操作。 比如將集合對象置為null, school.setStudentSet(null) inverse導致hibernate執(zhí)行:udpate STUDENT set SCHOOL_ID=null where SCHOOL_ID=? cascade則不會執(zhí)行對STUDENT表的關聯(lián)更新, 因為集合中沒有元素。 再比新增一個school, session.save(school) inverse導致hibernate執(zhí)行: for( 對(school的每一個student ){ udpate STUDENT set SCHOOL_ID=? where STUDENT_ID=? //將學生的school_id改為新的school的id } cascade導致hibernate執(zhí)行: for( 對school的每一個student ){ session.save(aStudent); //對學生執(zhí)行save操作 } extends:如果改變集合中的部分元素(比如新增一個元素), inverse: hibernate先判斷哪些元素改變了,對改變的元素執(zhí)行相應的sql cascade: 它總是對集合中的每個元素執(zhí)行關聯(lián)操作。 (在關聯(lián)操作中,hibernate會判斷操作的對象是否改變) 4.3.2 兩個起作用的時機不同: cascade:在對主控方操作時,級聯(lián)發(fā)生。 inverse: 在flush時(commit會自動執(zhí)行flush),對session中的所有set,hibernate判斷每個set是否有變化, 對有變化的set執(zhí)行相應的sql,執(zhí)行之前,會有個判斷: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方的數(shù)據(jù)庫表執(zhí)行update語句。 對many-to-many, hibernate對關系表執(zhí)行insert/update/delte語句,注意不是對many方的數(shù)據(jù)庫表而是關系表。 cascase 對set都是一致的,不管one-to-many還是many-to-many。都簡單地把操作傳遞到set中的每個元素。所以它總是更新many 方的數(shù)據(jù)庫表。 4.3.4 建議:只對set + many-to-many設置inverse=false,其他的標記不考慮inverse屬性,都設為inverse=true。 對cascade,一般對many-to-one,many-to-many,constrained=true的one-to-one 不設置級聯(lián)刪除。 引自:http://bbs.tech./simple/index.php?t144447.html |
|