這兩個屬性都用於一多對或者多對多的關係中。而inverse特別是用於雙向關係,在單向關係中我們並不需要。
Cascade代表是否執行級聯操作,Inverse代表是否由己方維護關係。
Cascade:
Cascade屬性的可能值有
all: 所有情況下均進行關聯操作,即save-update和delete。
none: 所有情況下均不進行關聯操作。這是默認值。
save-update: 在執行save/update/saveOrUpdate時進行關聯操作。
delete: 在執行delete 時進行關聯操作。
all-delete-orphan: 當一個節點在對象圖中成爲孤兒節點時,刪除該節點。比如在一個一對多的關係中,Student包含多個book,當在對象關係中刪除一個book時,此book即成爲孤兒節點。
Inverse:
Inverse屬性的可能值是true或者false,默認爲false:
false代表由己方來維護關係,true代表由對方來維護關係。在一個關係中,只能由一方來維護關係,否則會出問題(解疑中會講到);同時也必須由一方來維護關係,否則會出現雙方互相推卸責任,誰也不管。
一多對的例子:
有兩個類,Father和Child,是一對多的關係。下面這段hbm配置是從Father.hbm.xml中摘取的。
<set name="children" lazy="true" cascade="all" inverse="true"> <key column="fatherid"/> <one-to-many class="my.home.Child"/> </set>
我們知道cascade和inverse的值對會有四種組合的可能(在此僅先假定cascade值爲none或all)。
有如下一段代碼:
- FatherDao fatherDao = new FatherDao();
- Father father = new Father("David");
- Child child1 = new Child("David Junior One");
- Child child2 = new Child("David Junior Two");
- father.add(child1);
- father.add(child2);
- fatherDao.save(father);
FatherDao fatherDao = new FatherDao();
Father father = new Father("David");
Child child1 = new Child("David Junior One");
Child child2 = new Child("David Junior Two");
father.add(child1);
father.add(child2);
fatherDao.save(father);
1. 如果cascade="all"且inverse="false"時:
此時可以看到log裏面:
- // 執行對father的插入
- Hibernate: insert into father (name) values (?)
- // cascade = 'all',所以進行級聯操作
- Hibernate: insert into child (name, fatherid) values (?, ?)
- Hibernate: insert into child (name, fatherid) values (?, ?)
- // inverse = 'false',由father來維護關係(可以看到這些操作是多餘的)
- Hibernate: update child set fatherid =? where ID=?
- Hibernate: update child set fatherid =? where ID=?
// 執行對father的插入
Hibernate: insert into father (name) values (?)
// cascade = 'all',所以進行級聯操作
Hibernate: insert into child (name, fatherid) values (?, ?)
Hibernate: insert into child (name, fatherid) values (?, ?)
// inverse = 'false',由father來維護關係(可以看到這些操作是多餘的)
Hibernate: update child set fatherid =? where ID=?
Hibernate: update child set fatherid =? where ID=?
2. 如果cascade = "none" 且 inverse = "false":
- // 執行對father的插入
- Hibernate: insert into father (name) values (?)
- // inverse='false',所以更新關係
- Hibernate: update child set fatherid =? where ID=?
- // 但由於cascade='none',child並未插入數據庫,導致如下exception
- org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance
// 執行對father的插入
Hibernate: insert into father (name) values (?)
// inverse='false',所以更新關係
Hibernate: update child set fatherid =? where ID=?
// 但由於cascade='none',child並未插入數據庫,導致如下exception
org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance
3. 如果cascade = "all" 且 inverse = "true"
- // 執行對father的插入
- Hibernate: insert into father (name) values (?)
- // cascade='all',執行對child的插入
- Hibernate: insert into child (name, fatherid) values (?, ?)
- Hibernate: insert into child (name, fatherid) values (?, ?)
- // 但由於inverse='true',所以未有對關係的維護。但由於一對多的關係中,關係本身在“多”方的表中。所以,無需更新
- 關係。
// 執行對father的插入
Hibernate: insert into father (name) values (?)
// cascade='all',執行對child的插入
Hibernate: insert into child (name, fatherid) values (?, ?)
Hibernate: insert into child (name, fatherid) values (?, ?)
// 但由於inverse='true',所以未有對關係的維護。但由於一對多的關係中,關係本身在“多”方的表中。所以,無需更新
關係。
4. 如果cascade = "none" 且 inverse = "true"
- // 只執行對father的插入
- Hibernate: insert into father (name) values (?)
// 只執行對father的插入
Hibernate: insert into father (name) values (?)
可以看到,對於一對多關係,關係應由“多”方來維護(指定“一”方的inverse爲true),並且應在“一”方指定相應的級聯操作。
多對多:
在多對多關係中,inverse可以爲任何一方,沒有什麼區別。
解疑:
爲什麼在多對多中不能由雙方都來維護關係了:因爲這樣會導致重複更新中間表的可能,報出重複值的錯誤。
那麼如何在多對多的雙向關聯中使雙方都能維護關係:最好讓控制關係的那方來更新關係,如果想讓另一方也來維護關係,那麼只有在操作這一方的數據時“顯式”更新中間表了吧。
注意:
同時注意在雙向關聯中,對象之間的關聯跟上面提及的關係表維護沒有關係。一個是對象/java層面的,一個是hibernate數據庫層面的。如果你想在更新一方時,也更新另一方的對象集合,請看下面這段代碼:
這是Person類中的一段代碼,Person和Event是多對多的雙向關聯關係,他們在對方類中的集合分別爲participants和events。關係表由Person維護,所以對象關係的維護也在Person類中,而不是Event類中。
- public void addToEvent(Event event) {
- this.getEvents().add(event);
- event.getParticipants().add(this);
- }
- public void removeFromEvent(Event event) {
- this.getEvents().remove(event);
- event.getParticipants().remove(this);
- }