【http://blog.sina.com.cn/s/blog_625d79410101dbdd.html】
看過前兩篇幫助文檔 【JPA】 @OneToOne 單向 和 【JPA】@OneToOne 雙向 想必大家對級聯操作已經充滿了好奇和期待吧。那么本文將會想大家介紹JPA的級聯保存操作。在此之前,我希望你能先看下這篇文檔,對級聯注釋的各個屬性有一個大概的了解。傳送門:【JPA】 級聯標簽的解釋 @Cascade
在生活中,有許多關系都是一對多的。School(學校)和 Studnet(學生) 之間的關系就是典型的一對多關系。一所學校,有多名學生。但是一個學生,只能屬於一所學校。在這里,我們將這個關系設置為一個雙向的關系。也就說,通過學生實體,我們可以得到他就讀的學校的實體。同樣,通過一個學校實體,我們也可一得到在這所學校就讀的學生實體的集合。
--------< 例 子 >----------------------------------------------------------------------------------------------------------
//學生Model , 這個雙向一對多關系的維護端。一般來說,在雙向一對多關系中,"多"這一端,一般都是關系的維護端。
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
//在表中建立外鍵 "school_fk"
@ManyToOne
@JoinColumn(name="school_fk")
private School school;
//省略若干get / set
}
@Entity
@Table(name = "school")
public class School {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(mappedBy="shcool")
//設置 :級聯 保存/新建 操作 。新建 學校和學生 的時候,保存新建的學校那么新建的學生也同時被保存
@Column(cascade={CascadeType.PERSIST})
private List students;
//手動構造添加學生的方法
public void addStudent(Strudent stu){
if(stu != null){
students.add(stu);
}
}
//省略若干get / set
}
-----------------------------------------------------< 測試方法 >--------------------------------------------------------------
public class Test(){
public void testCreate(){
School school = new School();
school.setName("學校");
Student st1 = new Student();
st1.setName("學弟");
st1.setSchool(school);
studnetDAO.save(st1);
Student st2 = new Student();
st2.setName("學長");
st2.setSchool(school);
studnetDAO.save(st2);
//添加學生
school.addStudent(st1);
school.addStudent(st2);
schoolDAO.save(school);
//以上的保存方法並不是級聯保存操作。我們在學校實體上設置了,級聯保存操作。
//我們希望JPA這樣做,在新建學校,新建多名學生時。只要將學生的所屬學校設置完成后,保存學校,即可保存 所屬該校的學生。
}
//級聯保存,測試方法
public void testPersistCreateSchool(){
//級聯保存演示:已經在學校(School)Model 中添加了 級聯保存操作:cascade={CascadeType.PERSIST}
School school = new School();
school.setName("學校");
Student st1 = new Student();
st1.setName("學弟");
st1.setSchool(school);
// 不需要顯示保存學生 st1
//studnetDAO.save(st1);
Student st2 = new Student();
st2.setName("學長");
st2.setSchool(school);
// 不需要顯示保存學生 st2
//studnetDAO.save(st2);
//添加學生
school.addStudent(st1);
school.addStudent(st2);
schoolDAO.save(school);// 保存學校,學校內新建的學生也[st1、st2]被保存了。(級聯保存)
}
//測試刪除
public void testDeleteSchool(){
//革命第一步,創建學校。
testCreate();
schoolDAO.delete(school);//后台報錯
//avax.persistence.RollbackException: Error while commiting the transaction Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint...
//之前我們已經知道,想要刪除關系的被維護端,必須先從維護端解除關系。才能正常刪除。所以我們要做的就是,先從 學生端,解除關系。
List<Strudnet> students = school.getStudentList();
for(int i=0;i<students.size();i++){
Student stu = students.get(i);
stu.setSchool(null);//手動從學生實體解除掛系
}
schoolDAO.delete(school);//現在,學校被正常的刪除了。該校的兩名學生信息依然保存在數據庫中。只是,他們沒有所屬學校了。
//看到這里,你一定會責備我。JPA不是很強大么?不是很靈活多變么?不是很神奇么?
//為什么刪除一個實體,還需要循環解除關系,這么繁瑣的操作?!
//其實,我們有方法可以直接刪除學校。並且,不需要進行這種循環從學生實體解除關系的操作。但是,相信我,這種方法你可能不會接受。下面,就讓我們來看看JPA的級聯刪除。
//修改學校的Model 中,在校生字段()的注釋:@Column(cascade={CascadeType.PERSIST})
//改為:@Column(cascade={CascadeType.PERSIST,CascadeType.REMOVE}) 添加級聯刪除操作。那么,現在你就可以直接調用刪除學校的方法了。
schoolDAO.delete(school);//現在,學校也被正常刪除了。但是,接下來的一幕,你可能不願意看到。
Stduent stu = studentDAO.findByName(學長");
//打印的結果為: null 是的,你沒看錯。該校所有的學生也被級聯刪除了...
//此時,你應該對JPA的級聯刪除有所理解了。它刪除的對象是實體。級聯指的是級聯實體。如果,在學生的Model 中添加了一個級聯刪除的操作,那么刪除一個學生,那這個學生所屬的學校也會被刪除掉。
//所以,在這里,我要提醒你。級聯的刪除操作要慎用。因為,他的破壞性太大了。級聯刪除的操作,並不是針對某個字段,而是它所管理的實體。
}
//測試刪除
public void testDeleteStudent(){
//創建學校和學生
testCreate();
studentDAO.delete(st1);//此時,st1(創建時,名字叫“學弟”的學生)已經被從數據庫中刪除了。但是,他所屬的學校並沒受影響。只不過,st1已經從 學校的 學生集合里刪除了。這都是由JPA來完成的。
//現在,我們假設:我們沒有執行剛才那部。st1沒有被刪除。學校依然擁有兩名學生。
//在,Stuent的Model 中添加級聯刪除的操作。@JoinColumn(name="school_fk",cascade={CascadeType.REMOVE}) 並且,School 的 Model中也添加級聯刪除的操作。
//此時,數據庫中,只有一所學校和兩名學生的信息。
studentDAO.delete(st1);
//哇,全世界都干凈了。如果這個時候查看數據庫。你會發現,所有的數據都沒了。為什么會這樣呢?
//讓我們來看看JPA都干了什么:
1:刪除 st1
2:school 被 st1 級聯刪除
3:st2 被 school 級聯刪除
//所以,你才會發現,數據庫中所有的信息都消失了。當然,如果之前,數據庫中還有別的學校和別的學校所屬學生。那么它們的信息是不受影響的。
//羅嗦了這么多,我感到很抱歉。我只是想提醒你,一定要慎用這個級聯的刪除操作。千萬不用在建立Model 的時候,順手添加一個級聯保存 刪除之類的操作。一定要根據自己的需求。和操作的實際效果,仔細斟酌之后,再付諸於行動。否則,你直接來個CascadeType.ALL 大多數情況下,它帶給你的都是痛苦的回憶。
}
//省略main 方法
}