對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。也就是說:有級聯的情況下操作關係維護方才能成功。
所以我們將一的一方是設定級聯,多的一方不設定級聯,但設定為關係維護方是不合理的,我們可以將多的一方也設定級聯,但如果將多的一方的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快取中一的一方已經有了它所對應的所有多的一方,此時它不能被刪。