hibernate關聯對象的增刪改查------增,hibernate------
本文可作為,北京尚學堂馬士兵hibernate課程的學習筆記。
這一節,我們看看hibernate關聯關係的增刪改查
就關聯關係而已,咱們在上一節已經提了很多了,一對多,多對一,單向,雙向...
其實咱們可以簡單的說就是A與B,有關係。
至於他們到底是一對多,多對一,暫且不論。
咱們要討論的是,如果我儲存A,那麼資料庫裡是否會有B;如果我刪除A,那麼與之相關的B是否也會刪除;如果我更新了A,那麼B是否會被更新;如果我查詢出A,那麼B是否也會被查詢出來。
首先,咱們看一對多,多對一雙向的例子。
還是我們上一節的例子,dream與person。
一個person有多個dream,而每一個dream只能屬於一個person。
我們在配置xml時,讓hibernate自動產生建表語句,並且每次都新生資料庫。
<property name="hbm2ddl.auto">create</property>
實體類情況
@Entitypublic class Person { private int id; private String name; private Set<Dream> dreams=new HashSet<>(); @Id @GeneratedValue public int getId() { return id; } @Column(name ="myname") public String getName() { return name; } @OneToMany(mappedBy="person") public Set<Dream> getDreams() { return dreams; }//省略部分代碼}@Entitypublic class Dream { private int id; private String description; private Person person; @Id @GeneratedValue public int getId() { return id; } @ManyToOne @JoinColumn(name="personId") public Person getPerson() { return person; } //省略部分代碼}
OK,兩個類的關係已經很清楚了,雙向多對一/一對多。
如果我們的測試代碼如下:
//代碼片1
public static void main(String[] args) { Person p=new Person(); p.setName("dlf"); Dream d=new Dream(); d.setDescription("marry glt"); d.setPerson(p); SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(p); session.save(d); session.getTransaction().commit(); }
上面的代碼是沒有問題的,而且即使先save dream也OK。
那麼現在問題來了
d.setPerson(p);
這句代碼,已經說明了dream裡有一個引用指向了person。
那麼我可以在代碼層次不save person麼?
即只有一個
session.save(d);
答案是否定的,報下面的錯誤。
object references an unsaved transient instance - save the transient instance before flushing: com.bjsxt.hibernate.Person
因為在資料庫儲存dream的時候,person還沒有儲存,所以出錯了。
上面的例子告訴我們,預設情況下:hibernate不會替我們儲存"一"的那一方。
當我寫到這塊的時候,我忽然腦子一抽,又寫下了下面的測試代碼:
//代碼片2public static void main(String[] args) { Dream d=new Dream(); d.setDescription("marry glt"); Person p=new Person(); p.setName("dlf"); p.getDreams().add(d); SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(p); session.getTransaction().commit(); }
如果,我以"一"的那一方為主,上面的代碼會是個什麼情況?
答案是,不報錯,代碼能正常執行,但是資料庫裡只有p的資訊而沒有d的資訊。
上面的代碼確實有點奇葩了,在上一節的時候,我們就已經達成了共識,在處理關聯關係時,資料庫層面總是在多的那一方維護關係。
所以上面的代碼,並不能儲存dream。
上面也說了,是在預設情況下,那特殊情況呢?
我們看api文檔
上面是manytoone這個元標籤可以帶的屬性,cascade翻譯成中文就是級聯。他是caschdetype型的
而Caschdetype是enum型的。可以取六個值
我們最經常用的就是,all,persist與remove。
all的意思是,對這個對象的增刪改四中操作都會影響到與之關聯的那個對象。(關於查詢的級聯,由另一個屬性管理)
persist的意思是,對這個對象的增加(save操作)會連帶把與之關聯的那個對象也sava了。
remove的意思就是,如果刪除這個對象也會刪除與之關聯的那個對象。
OK看下面的改動
@ManyToOne(cascade={CascadeType.ALL}) @JoinColumn(name="personId") public Person getPerson() { return person; }
此時,我們下面的代碼,就能正常運行了,而且資料庫裡,兩個對象都儲存了,且外部索引鍵關聯也是OK的。
//代碼片3 Person p=new Person(); p.setName("dlf"); Dream d=new Dream(); d.setDescription("marry glt"); d.setPerson(p); session.save(d);
寫到這,我的腦子又抽了,如果我給onetomany的那一方加上cascade會如何?
@OneToMany(mappedBy="person",cascade={CascadeType.ALL}) public Set<Dream> getDreams() { return dreams; } 測試代碼: Dream d=new Dream(); d.setDescription("marry glt"); Person p=new Person(); p.setName("dlf"); p.getDreams().add(d); session.save(p);
cascade確實有用,此時資料庫裡已經有了dream,但是dream的外鍵是null。
為什麼?因為dream裡並沒有關聯person。
其實,cascade這個東西也不是絕對的,如果關聯關係比較麻煩,我們探索資料庫裡總是少存了一個對象,那就按照代碼1的辦法,直接存兩次對象不就OK了。
談點規律:
1 我們發現如果儲存物件的時候,我們從多的一方操作是比較簡單的。所以以後,盡量在多的一方操作。
2 為了符合邏輯,同時也不出錯,一點兩個對象之間的關係是雙向的,那麼在代碼層次,就把兩個的引用都設好,這樣就不會出問題了。
3 之前說的,如果是雙向的,設定mappedby。