1. 程式人生 > >對Hibernate中關係維護方和級聯的理解

對Hibernate中關係維護方和級聯的理解

級聯:在雙向多對一中,有一方設定了級聯,如@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)(它是在多的一方設定了級聯),它的作用就是在操作設定級聯的那一方時會影響另一方(比如在儲存時,當然前提是要對對方屬性有設定,不然就是空,那麼對方也會插入資料,但對方插入資料時,有沒有本方的屬性就要看本方是不是關係維護方了)。(如果某一方沒有設定級聯,那麼它在操作時不會影響另一方)

關係維護方:在雙向多對一中,一般會設定多的一方為關係維護方。比如@OneToMany(fetch = FetchType.LAZY, mappedBy = "tblUser")(註解方式不允許一的一方設為關係維護方)。關係維護方是指在該方做增刪改時,會去維護另一方。以Order和User為例,當有order.setUser(user),且user沒有setOrders()時,其中Order為關係維護方,那麼當session.save(order)時,會去維護User物件,但此時如果Order沒有開啟級聯操作,那麼就會報錯org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing。也就是說:有級聯的情況下操作關係維護方才能成功。

當然,如果order沒有setUser,那麼不需要級聯也是可以的。當多的一方滿足1、關係維護方和2、設定了級聯屬性。那麼在做save操作前,只需要對多的一方做設定(如order.setUser(user))就可以了,不需要一的一方做設定(即不需要user.setOrders())。所以如果是一的一方做save操作時,必須兩方都做設定(order.setUser(user)和user.setOrders())。因為在註解中,一的一方不能滿足它是關係維護方這個條件。非關係維護方在有無級聯的情況下都能操作本方物件。

所以我們將一的一方是設定級聯,多的一方不設定級聯,但設定為關係維護方是不合理的,我們可以將多的一方也設定級聯,但如果將多的一方的cascade 設為 CascadeType.ALL,那麼如果刪除多的一方,會將一的一方也刪除,所以設為 cascade = {CascadeType.PERSIST,CascadeType.REFRESH}即可。

注意一個問題:

多的一方預設使用飢餓載入,一的一方預設使用懶載入。當多的一方使用delete時,沒有級聯操作時,即使是維護方(維護方一定要指定)也能做刪除操作。但是如果此時一的一方使用飢餓載入,那麼多的一方使用delete時會報錯:org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)。

那怎麼辦?解除要刪除物件的多對一關係,兩方都要設定,因為此時一的一方已經載入了多的一方,做法比如(order.getTblUser().getTblOrders().remove(order);
order.setTblUser(null);)。本來我以為維護方只要設定order.setTblUser(null)即可。但因為session快取中一的一方已經有了它所對應的所有多的一方,此時它不能被刪。