一.多對多雙向關聯java
以Student和Course爲例,一個學生能夠選多門課程,一門課程也能夠被多個學生選取mysql
持久化類Studentsql
1 持久化類Student.java 2 3 package com.edu.many2many; 4 5 import java.util.HashSet; 6 import java.util.Set; 7 8 public class Student { 9 10 private Integer id; 11 private String name; 12 13 //關聯關係 14 Set<Course> courses = new HashSet<Course>(); 15 public Integer getId() { 16 return id; 17 } 18 public void setId(Integer id) { 19 this.id = id; 20 } 21 public String getName() { 22 return name; 23 } 24 public void setName(String name) { 25 this.name = name; 26 } 27 public Set<Course> getCourses() { 28 return courses; 29 } 30 public void setCourses(Set<Course> courses) { 31 this.courses = courses; 32 } 33 34 35 }
持久化類Course數據庫
1 持久化類Course.java 2 3 package com.edu.many2many; 4 5 import java.util.HashSet; 6 import java.util.Set; 7 8 public class Course { 9 10 private Integer id; 11 private String name; 12 13 //關聯關係 14 Set<Student> students = new HashSet<Student>(); 15 16 public Integer getId() { 17 return id; 18 } 19 public void setId(Integer id) { 20 this.id = id; 21 } 22 public String getName() { 23 return name; 24 } 25 public void setName(String name) { 26 this.name = name; 27 } 28 public Set<Student> getStudents() { 29 return students; 30 } 31 public void setStudents(Set<Student> students) { 32 this.students = students; 33 } 34 35 36 }
Student.hbm.xml對象關係映射文件session
1 Student.hbm.xml映射文件 2 3 <?xml version="1.0" encoding="UTF-8"?> 4 <!DOCTYPE hibernate-mapping PUBLIC 5 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 6 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 7 <hibernate-mapping package="com.edu.many2many"> 8 <class name="Student"> 9 <id name="id"> 10 <generator class="increment"/> 11 </id> 12 13 <property name="name"/> 14 15 <!-- 關聯屬性 16 cascade屬性:級聯操做默認值爲none 17 save-update:級聯保存或更新 18 delete:級聯刪除 19 all 20 inverse="true":默認值爲false。主控方 21 true 反轉控制權,變爲被控方,沒有維護關聯關係的權利。 22 --> 23 <!-- courses 和Course類 多對多 24 在多對多關係中,映射兩端關係表的名稱必需要相同 25 --> 26 <set name="courses" table="stu_cou" > 27 <key column="sid"/> 28 <many-to-many class="Course" column="cid"/> 29 </set> 30 </class> 31 32 33 </hibernate-mapping>
多對多關聯關係的實現須要一個鏈接表,<set>的屬性指出的就是鏈接表的名稱,<key>指出鏈接表參照students表id的外鍵的字段名;<many-to-many>中的class指定與Student多對多關聯的類,column指定鏈接表參照Course映射表(此處由Course.hbm.xml映射爲courses表)id的外鍵的字段名,Course.hbm.xml中的<set>配置與Student.hbm.xml中<set>相反oracle
Course.hbm.xml對象關係映射文件app
1 Course.hbm.xml映射文件 2 3 <?xml version="1.0" encoding="UTF-8"?> 4 <!DOCTYPE hibernate-mapping PUBLIC 5 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 6 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 7 <hibernate-mapping package="com.edu.many2many"> 8 <class name="Course" table="course"> 9 <id name="id"> 10 <generator class="increment"/> 11 </id> 12 13 <property name="name"/> 14 15 <!-- 映射關聯屬性 --> 16 <!-- students 和 Student類 多對多 17 name: 關聯屬性的名字 18 class: 關聯類的類名 19 column: 數據庫外鍵字段的名字 20 table:關係表的名稱 21 22 多對多關聯映射 中cascade="delete" 注意使用:會級聯刪除對端表的記錄 23 --> 24 <set name="students" table="stu_cou" > 25 <key column="cid"/> 26 <many-to-many class="Student" column="sid"/> 27 </set> 28 </class> 29 30 </hibernate-mapping>
兩個映射文件中設置的鏈接表的名稱以及鏈接表中的兩個字段名需對應相同,如鏈接表名都爲"stu_cou"兩字段爲"sid"和"cid",不然會致使沒必要要的麻煩;鏈接表的主鍵爲聯合主鍵(sid,cid)工具
hibernate.cfg.xml映射文件單元測試
1 hibernate.cfg.xml映射文件 2 3 <?xml version="1.0" encoding="UTF-8"?> 4 <!DOCTYPE hibernate-configuration PUBLIC 5 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 6 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 7 <hibernate-configuration> 8 <session-factory> 9 <!-- 配置數據庫鏈接的基本信息 --> 10 <property name="hibernate.connection.driver_class"> 11 oracle.jdbc.driver.OracleDriver 12 </property> 13 <property name="hibernate.connection.url"> 14 jdbc:oracle:thin:@localhost:1521:XE 15 </property> 16 <property name="hibernate.connection.username">lihengyu</property> 17 <property name="hibernate.connection.password">lihengyu</property> 18 19 <!-- 數據庫的方言 使用的是哪一種數據庫 --> 20 <property name="hibernate.dialect"> 21 org.hibernate.dialect.Oracle10gDialect 22 </property> 23 <!-- 顯示sql語句 --> 24 <property name="hibernate.show_sql">true</property> 25 <!-- 自動建立數據庫表 26 none:默認值 有表就使用,沒表不建立 27 create:每次都建立新表 28 update: 有表就使用,沒表建立。若是持久化類及映射文件發生改變,則發生alter語句修改表結構。 29 --> 30 <property name="hibernate.hbm2ddl.auto">update</property> 31 32 <!-- 添加映射文件 --> 33 <mapping resource="com/edu/many2many/Course.hbm.xml" /> 34 <mapping resource="com/edu/many2many/Student.hbm.xml" /> 35 36 37 </session-factory> 38 39 40 </hibernate-configuration>
TestHibernate.java測試代碼測試
1 TestHibernate .java 2 3 package com.edu.many2many; 4 5 import org.hibernate.SessionFactory; 6 import org.hibernate.Transaction; 7 import org.hibernate.cfg.Configuration; 8 import org.hibernate.classic.Session; 9 import org.junit.Test; 10 11 /** 12 * 使用單元測試工具,測試hibernate 的增刪改查(CRUD)操做 13 * 14 * @author Administrator 15 * 16 */ 17 public class TestHibernate { 18 19 private static SessionFactory sf; 20 static { 21 // 1. 加載配置文件 22 Configuration cfg = new Configuration(); 23 cfg.configure("com/edu/many2many/hibernate.cfg.xml"); 24 25 // 2. 得到SessionFactory 26 sf = cfg.buildSessionFactory(); 27 } 28 29 30 /** 31 * 保存用戶 32 * 33 */ 34 @Test 35 public void save() { 36 37 38 Session session = sf.openSession(); 39 Transaction tran = session.beginTransaction(); 40 41 Course c1 = new Course(); 42 c1.setName("體育"); 43 Course c2 = new Course(); 44 c2.setName("音樂"); 45 46 Student s1 = new Student(); 47 s1.setName("張三"); 48 Student s2 = new Student(); 49 s2.setName("李四"); 50 51 //維護關聯關係 52 c1.getStudents().add(s1); 53 c1.getStudents().add(s2); 54 55 c2.getStudents().add(s1); 56 c2.getStudents().add(s2); 57 /** 58 * 若是程序中,須要從關聯兩端同時維護關係,則其中一端必須交出控制權 59 * 變爲被控方,被控方不在維護關聯關係 60 */ 61 s1.getCourses().add(c1); 62 s1.getCourses().add(c2); 63 64 s2.getCourses().add(c1); 65 s2.getCourses().add(c2); 66 67 session.save(c1); 68 session.save(c2); 69 session.save(s1); 70 session.save(s2); 71 72 tran.commit(); 73 session.close(); 74 } 75 76 /** 77 * 查詢 78 * 79 */ 80 @Test 81 public void find() { 82 83 84 Session session = sf.openSession(); 85 Transaction tran = session.beginTransaction(); 86 87 //誰選擇了id=1的課程 88 Course c1 = (Course) session.get(Course.class, 1); 89 for (Student stu : c1.getStudents()) { 90 System.out.println(stu.getName()); 91 } 92 93 tran.commit(); 94 session.close(); 95 } 96 97 //刪除1號課程 98 @Test 99 public void delete() { 100 101 102 Session session = sf.openSession(); 103 Transaction tran = session.beginTransaction(); 104 105 106 Course c1 = (Course) session.get(Course.class, 1); 107 108 session.delete(c1); 109 110 //若是student是被控方,不能級聯刪除外表記錄 111 /*Student s1 = (Student) session.get(Student.class, 1); 112 session.delete(s1);*/ 113 114 tran.commit(); 115 session.close(); 116 } 117 }
(1)若是兩個映射文件的inverse都設爲false(默認),則會出現異常(主鍵重複)致使插入失敗:
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
Caused by: Java.sql.BatchUpdateException: Duplicate entry '1-1' for key 'PRIMARY'
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:Duplicate entry '1-1' for key 'PRIMARY'
解釋:應爲兩映射文件中的inverse都爲true,則Student和Course都去維護關聯關係,即同時向鏈接表中插入記錄,則會致使主鍵重複而插入失敗。
解決辦法:
——將其中一方的inverse設爲true,讓對方維持關聯關係;
——將s1.getCourses().add(c1);或 c1.getStudents().add(s1);刪除,由於若某個Course中的students集合爲空時,它就不會去向鏈接表中添加記錄,也就不會與Student向鏈接表中插入記錄時衝突而主鍵重複。
(2)若是都設爲true,則都不會向鏈接表中插入記錄而只是向兩表中插入記錄(二者都認爲對方會維持關聯關係)
(3)若是不是Student維持關聯關係:
——若鏈接表stu_cou中有參照students表中該記錄的記錄(即在stu_cou表中存在sid爲2的記錄)時,則刪除失敗。
——若鏈接表stu_cou中沒有參照students表中該記錄的記錄時,則能夠成功地將該記錄刪除。
(4)若是是Student維持關聯關係:
——先將鏈接表stu_cou中參照students表中該記錄的記錄刪除,而後將該學生記錄從students表中刪除。