Hibernate關係對映-級聯操作
在雙向一對多的學習中,我們發現每次儲存物件時,學生物件和年紀物件都需要我們持久化之session,既然它們兩者有關聯關係,可不可以只持久化其中一端,另一端就會自動的被持久化呢,這個屬性就是Hibernate的cascade。cascade是多對一、一對多、一對一、多對多各種關係之間的一種級聯操作行為。(關鍵語句:cascade="save-update"。)
一、什麼是級聯操作
官方解釋:必須級聯到關聯目標的操作,預設情況下沒有級聯操作。
當Hibernate持久化一個瞬時物件時,在預設情況下,它不會自動持久化所關聯的其他臨時物件,而是會丟擲org.hibernate.TransientObjectException。
級聯的意思是:本實體做了什麼事,也要拉上另一個關聯的實體,導致另一個實體跟著做事情。就是說我刪除了,你也必須刪除!關聯目標,指的是關聯的那個實體 。
二、準備案例
在明確了級聯操作的概念知識以後,使用學生表和年紀表的雙向一對多關係,實現級聯操作行為。
學生表和年紀表的雙向一對多關係學習請參考(Hibernate關係對映-雙向一對多)。
cascade級聯操作,預設是none不級聯。
none:不級聯。
save-update:儲存和更新某一端資料時,另一端資料可以一起儲存和更新。
delete:刪除級聯-不能在多的一端使用。
all:表示所有操作都級聯
三、實現級聯操作
1、配置多端
-
編寫配置檔案
Student.hbm.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.uestc"> <class name="Student"> <
2.編寫Grade.hbm.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.uestc"> <class name="Grade"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!-- 單向一對多關係 set:Grade類中的集合屬性 name:集合屬性名稱 key:外來鍵 column:外來鍵列名 foreign-key:生成外來鍵約束的名字(可以不寫,預設隨機) not-null="true" 不可以為空(可以不寫,預設false) one-to-many:Grade類中屬性students所表示型別 --> <set name="students" cascade="save-update"> <key foreign-key="fk_grade" column="grade_id"></key> <one-to-many class="Student"/> </set> </class> </hibernate-mapping>
-
Student類
public class Student implements Serializable { private Integer id; private String name; private Integer age; private Grade grade; @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", grade=" + grade + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Grade getGrade() { return grade; } public void setGrade(Grade grade) { this.grade = grade; } }
-
Grade類
public class Grade implements Serializable { private Integer id; private String name; private Set<Student> students = new HashSet<>(); @Override public String toString() { return "Grade{" + "id=" + id + ", name='" + name + '\'' + ", students=" + students + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } }
-
編寫hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 初始化JDBC連線 --> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql:///hibernate</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- 方言 --> <property name="dialect">org.hibernate.dialect.MySQL8Dialect</property> <!-- 資料庫生成方式 --> <property name="hbm2ddl.auto">update</property> <!-- validate檢測,create-drop刪了重新建立,create重新建表,update表如果存在插資料,如果不存在建表插資料 --> <!-- 列印sql --> <property name="show_sql">true</property> <!-- 格式化sql --> <property name="format_sql">true</property> <!-- 關聯物件配置檔案 --> <mapping resource="com/uestc/Grade.hbm.xml"/> <mapping resource="com/uestc/Student.hbm.xml"/> </session-factory> </hibernate-configuration>
-
測試類
/** * 單向多對一 */ @Test public void testSingleManyToOne() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); //建立例項物件 Grade grade1 = new Grade(); grade1.setName("基礎"); Grade grade2 = new Grade(); grade2.setName("中級"); Student student = new Student(); student.setName("張三"); student.setAge(18); student.setGrade(grade1); Student student2 = new Student(); student2.setName("李四"); student2.setAge(18); student2.setGrade(grade1); Student student3 = new Student(); student3.setName("王五"); student3.setAge(18); student3.setGrade(grade2); //儲存的順序是根據外來鍵約束而定的,如果外來鍵不可以為空,必須先儲存外來鍵的一端 //如果外來鍵可以為空,隨意儲存,但是建議先儲存外來鍵的一端,因為會多執行update // session.save(grade1); // session.save(grade2); session.save(student); session.save(student2); session.save(student3); tx.commit(); }catch (Exception e){ e.printStackTrace(); tx.rollback(); }finally { HibernateUtil.closeSession(); } } /** * 獲取 * 查詢不用開啟事務,會降低效能 */ @Test public void testSingleGetManyToOne() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); Student student = session.get(Student.class,1); System.out.println("stuName:" +student.getName() + ",grade:" +student.getGrade().getName()); tx.commit(); }catch (Exception e){ e.printStackTrace(); tx.rollback(); }finally { HibernateUtil.closeSession(); } } /** * 單向一對多 */ @Test public void testSingleOneToMany() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); //建立例項物件 Student student = new Student(); student.setName("張三"); student.setAge(18); Student student2 = new Student(); student2.setName("李四"); student2.setAge(18); Student student3 = new Student(); student3.setName("王五"); student3.setAge(18); Grade grade1 = new Grade(); grade1.setName("基礎"); grade1.getStudents().add(student); grade1.getStudents().add(student2); Grade grade2 = new Grade(); grade2.setName("中級"); grade2.getStudents().add(student3); //儲存的順序是根據外來鍵約束而定的,如果外來鍵不可以為空,必須先儲存外來鍵的一端 //單向一對多會多執行update語句,效率不如多對一 session.save(grade1); session.save(grade2); // session.save(student); // session.save(student2); // session.save(student3); tx.commit(); }catch (Exception e){ e.printStackTrace(); tx.rollback(); }finally { HibernateUtil.closeSession(); } } /** * 獲取 * 查詢不用開啟事務,會降低效能 */ @Test public void testSingleGetOneToMany() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); Student student = session.get(Student.class,1); System.out.println("stuName:" +student.getName() + ",grade:" +student.getGrade().getName()); System.out.println("---------------------分割線-------------------"); Grade grade = session.get(Grade.class,1); System.out.println("grade:" +grade.getName()); for(Student stu : grade.getStudents()){ System.out.println("stuName:" +stu.getName()); } tx.commit(); }catch (Exception e){ e.printStackTrace(); tx.rollback(); }finally { HibernateUtil.closeSession(); } }
-
-
-
四、總結
大家可以嘗試除了新增以外的其他操作,可以得出以下結論
-
在多對一的關係中,多的一端不能操作級聯為delete,一般在多的一端設為save-update;
-
在一對多的關係中,如果一的一端設為delete相關配置時,多的一端不能指明外來鍵為非空。