1、到底在哪用cascade="..."?java
cascade屬性並非多對多關係必定要用的,有了它只是讓咱們在插入或刪除對像時更方便一些,只要在cascade的源頭上插入或是刪除,全部cascade的關係就會被本身動的插入或是刪除。即是爲了能正確的cascade,unsaved-value是個很重要的屬性。Hibernate經過這個屬性來判斷一個對象應該save仍是update,若是這個對象的id是unsaved-value的話,那說明這個對象不是persistence object要save(insert);若是id是非unsaved-value的話,那說明這個對象是persistence object(數據庫中已存在),只要update就好了。saveOrUpdate方法用的也是這個機制。web
2、到底在哪用inverse="ture"?sql
inverse屬性默認是false的,就是說關係的兩端都來維護關係。這個意思就是說,若有一個Student, Teacher和TeacherStudent表,Student和Teacher是多對多對多關係,這個關係由TeacherStudent這個表來表現。那麼何時插入或刪除TeacherStudent表中的記錄來維護關係呢?在用hibernate時,咱們不會顯示的對TeacherStudent表作操做。對TeacherStudent的操做是hibernate幫咱們作的。hibernate就是看hbm文件中指定的是"誰"維護關係,那個在插入或刪除"誰"時,就會處發對關係表的操做。前提是"誰"這個對象已經知道這個關係了,就是說關係另外一頭的對象已經set或是add到"誰"這個對象裏來了。前面說過inverse默認是false,就是關係的兩端都維護關係,對其中任一個操做都會處發對錶系表的操做。當在關係的一頭,如Student中的bag或set中用了inverse="true"時,那就表明關係是由另外一關維護的(Teacher)。就是說當這插入Student時,不會操做TeacherStudent表,即便Student已經知道了關係。只有當Teacher插入或刪除時纔會處發對關係表的操做。因此,當關系的兩頭都用inverse="true"是不對的,就會致使任何操做都不處發對關係表的操做。當兩端都是inverse="false"或是default值是,在代碼對關係顯示的維護也是不對的,會致使在關係表中插入兩次關係。數據庫
在一對多關係中inverse就更有意義了。在多對多中,在哪端inverse="true"效果差很少(在效率上)。可是在一對多中,若是要一方維護關係,就會使在插入或是刪除"一"方時去update"多"方的每個與這個"一"的對象有關係的對象。而若是讓"多"方面維護關係時就不會有update操做,由於關係就是在多方的對象中的,直指插入或是刪除多方對象就好了。固然這時也要遍歷"多"方的每個對象顯示的操做修關係的變化體現到DB中。無論怎樣說,仍是讓"多"方維護關係更直觀一些。緩存
3、cascade和inverse有什麼區別?session
能夠這樣理解,cascade定義的是關係兩端對象到對象的級聯關係;而inverse定義的是關係和對象的級聯關係。app
all : 全部狀況下均進行關聯操做。
none:全部狀況下均不進行關聯操做。這是默認值。
save-update:在執行save/update/saveOrUpdate時進行關聯操做。
delete:在執行delete時進行關聯操做。 spa
all的意思是save-update + delete
all-delete-orphan 的意思是當對象圖中產生孤兒節點時,在數據庫中刪除該節點
all比較好理解,舉個例子說一下all-delete-orphan:
Category與Item是一對多的關係,也就是說Category類中有個Set類型的變量items.
舉個例子,現items中存兩個Item, item1,item2,若是定義關係爲all-delete-orphan
當items中刪除掉一個item(好比用remove()方法刪除item1),那麼被刪除的Item類實例
將變成孤兒節點,當執行category.update(),或session.flush()時
hibernate同步緩存和數據庫,會把數據庫中item1對應的記錄刪掉hibernate
//////////////////////////////////////////////////////////////////////////////////////////////////////////orm
///////////////////////////////////////////////////////////////////////////////////////////////////////////
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_SCHOOLwhere ADDRESS_ID=? AND SCHOOL_ID=?(33)delete all的SQL語句爲: delete from ADDRESS_SCHOOLwhere 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.saveOrUpdatesession.udpate --> session.saveOrUpdatesession.delete --> session.delete4.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仍是saveextends: 實際中,刪除學校不會刪除地區,即地區的cascade通常設爲false另外,many-to-many關係不多設置cascade=true,而是設置inverse=false。這個反映了cascade和inverse的區別。見4.34.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先判斷哪些元素改變了,對改變的元素執行相應的sqlcascade: 它老是對集合中的每一個元素執行關聯操做。(在關聯操做中,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 不設置級聯刪除。