在hibernate中,inverse和cascade常常容易弄混淆,下面簡單的說明一下二者區別。
cascade:
這裏咱們以annotation做爲例子。
cascade={CascadeType.ALL}
CascadeType是枚舉類型,其取值能夠是:
- ALL Cascade all operations全部狀況
- MERGE Cascade merge operation合併(merge=save+update)
- PERSIST Cascade persist operation存儲 persist()
- REFRESH Cascade refresh operation刷新
- REMOVE Cascade remove operation刪除
拿persist做爲例子,當把一個對象存進數據庫時,若是這個對象的cascade設爲persist那麼,與它相對應的那個對象能夠自動被保存進數據庫,而不須要顯示操做。其餘操做類同。PS:對於annotation來講,cascade所在的類的行爲是」主動級聯「,cascade所標示的成員屬性是」被動級聯「。對於insert操做來講,插入一個主動級聯的對象,會自動將被動級聯的對象插入。
inverse(至關於annotation中的mappedBy):
inverse是比較容易迷糊的一個屬性,這個屬性出如今hbm.xml文件中,annotation沒有。
在many-to-many中:
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值是,在代碼對關係顯示的維護也是不對的,會致使在關係表中插入兩次關係。
在one-to-many中
在一對多關係中inverse就更有意義了。在多對多中,在哪端inverse="true"效果差很少(在效率上)。可是在一對多中,若是要一方維護關 系,就會使在插入或是刪除"一"方時去update"多"方的每個與這個"一"的對象有關係的對象。而若是讓"多"方面維護關係時就不會有update 操做,由於關係就是在多方的對象中的,直指插入或是刪除多方對象就好了。固然這時也要遍歷"多"方的每個對象顯示的操做修關係的變化體現到DB中。無論 怎樣說,仍是讓"多"方維護關係更直觀一些。
PS:能夠作一個簡單的one-to-many實驗,設「一」的一方cascade=「save-update」,inverse=「false」。這樣對「一」進行操做就會級聯操做「多」方,而且由「一」維護關係。插入數據時會發現,在「多」方有多餘的update操做,這是「一」方維護關係的結果。
inverse的實際效果就是減小沒必要要的SQL語句
cascade和inverse有什麼區別?
能夠這樣理解,cascade定義的是關係兩端對象到對象的級聯關係;而inverse定義的是關係和對象的級聯關係。
inverse只對set+one-to-many(或many-to-many)有效,對many-to-one, one-to-one無效。cascade對關係標記都有效。
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會判斷操做的對象是否改變)
兩個起做用的時機不一樣:
cascade:在對主控方操做時,級聯發生。
inverse: 在flush時(commit會自動執行flush),對session中的全部set,hibernate判斷每一個set是否有變化,
對有變化的set執行相應的sql,執行以前,會有個判斷:if( inverse == true ) return;能夠看出cascade在先,inverse在後。
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方的數據庫表。
cascade和inverse有什麼相同?
這兩個屬性自己互不影響,但起的做用有些相似,都能引起對關係表的更新。(
可是,teacher和student爲例,若是隻設置inverse而不設置cascade,存儲teacher時並不會保存student,並且會報錯!)
建議:只對set + many-to-many設置inverse=false,其餘的標記不考慮inverse屬性,都設爲inverse=true。對cascade,一 般對many-to-one,many-to-many,constrained=true的one-to-one 不設置級聯刪除。
異常1:not-null property references a null or transient value
解決方法:將「一對多」關係中的「一」方,not-null設置爲false
(參考資料:http://www.thearcmind.com/confluence/pages/viewpage.action?pageId=212)
異常2:org.hibernate.TransientObjectException: object references an unsaved transient instance
解決方法:cascade="save-update,persist"
(參考資料:http://www.laliluna.de/254.html)
異常3:org.hibernate.QueryException: could not resolve property
解決方法:"from Category category where category.userID = :userID"修改成"from Category category whereuserID = :userID"或者"from Category category where category.user.id = :userID"
(參考資料:http://www.laliluna.de/277.html)
異常4:could not initialize proxy - the owning Session was closed
解決方法:設置lazy爲false
(參考資料:http://forum.springframework.org/showthread.php?t=27993) php
異常2我在應用中碰到了這樣的問題: html
有三個表:userInfo deptmentInfo role spring
userInfo 與deptmentInfo爲many to one sql
userInfo 與role爲many to one 數據庫
下面爲保存UserInfo對象時的代碼: session
DeptmentInfo dept = new DeptmentInfo();
dept.setDeptName(deptName); app
Role role = new Role();
role.setRoleName(roleName); dom
UserInfo user = new UserInfo();
user.setUserName(userName);
user.setUserSex(userSex);
user.setDuty(duty);
user.setPhone(phone);
user.setMobileNum(mobileNum);
user.setEmail(email);
user.setQq(qq);
user.setMsn(msn);
user.setAdress(adress);
user.setDeptmentInfo(dept);
user.setRole(role); fetch
dao.save(user); spa
執行時有錯:org.hibernate.TransientObjectException: object references an unsaved transient instance
因而改了UserInfo.hbm.xml的一些地方
以下:
<many-to-one name="role" class="com.oa.domain.Role"
cascade="save-update,persist" fetch="select">
<column name="ROLE_ID" precision="22" scale="0" />
</many-to-one>
<many-to-one name="deptmentInfo"
class="com.oa.domain.DeptmentInfo" cascade="save-update,persist"
fetch="select">
<column name="DEPT_ID" precision="22" scale="0" />
</many-to-one>
加了上面紅色部分的,就OK了,能保存了。