從生疏到熟練 是要經歷多少遍的練習? 這答案只能向自己找。
以Student和Course為例,一個學生可以選多門課程,一門課程也可以被多個學生選取;
首先 我們創建持久化類Student
1 package bean; 2 3 import java.util.Set; 4 5 public class Student { 6 private long id; 7 private String name;//學生姓名 8 private Set<Course> courses;//該學生選擇的課程 9 //省略set、get方法 10 }
接下來就是持久化類Course
1 package bean; 2 3 import java.util.Set; 4 5 public class Course { 6 private long id; 7 private String name;//課程名稱 8 private Set<Student> students;//選擇該課程的學生 9 //省略set、get方法 10 }
然後是對象關系映射文件Student.hbm.xml:
1 <hibernate-mapping> 2 <class name="bean.Student" table="students"> 3 <id name="id" column="id" type="long"> 4 <generator class="increment"></generator> 5 </id> 6 <property name="name" column="name" type="string"></property> 7 <set name="courses" table="students_courses" cascade="save-update"> 8 <key column="student_id"></key> 9 <many-to-many class="bean.Course" column="course_id"></many-to-many> 10 </set> 11 </class> 12 </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>相反:
Course.hbm.xml:
1 <hibernate-mapping> 2 <class name="bean.Course" table="courses"> 3 <id name="id" column="id" type="long"> 4 <generator class="increment"></generator> 5 </id> 6 <property name="name" column="name" type="string"></property> 7 <set name="students" table="students_courses" cascade="save-update" inverse="true"> 8 <key column="course_id"></key> 9 <many-to-many class="bean.Student" column="student_id"></many-to-many> 10 </set> 11 </class> 12 </hibernate-mapping>
注意:兩個映射文件中設置的連接表的名稱以及連接表中的兩個字段名需對應相同,如連接表名都為"students_courses"兩字段為"student_id"和"course_id",否則會導致不必要的麻煩;連接表的主鍵為聯合主鍵(student_id,course_id)。
三個表的結構及對應關系如下所示:
接著就是保存對象:
1 Student s1=new Student(); 2 s1.setName("lisi"); 3 Course c1=new Course(); 4 c1.setName("English"); 5 Course c2=new Course(); 6 c2.setName("science"); 7 s1.setCourses(new HashSet<Course>()); 8 c1.setStudents(new HashSet<Student>()); 9 c2.setStudents(new HashSet<Student>()); 10 s1.getCourses().add(c1); 11 s1.getCourses().add(c2); 12 c1.getStudents().add(s1); 13 c2.getStudents().add(s1); 14 15 session.save(c1); 16 session.save(s1);
(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,則都不會向連接表中插入記錄而只是向兩表中插入記錄(兩者都認為對方會維持關聯關系)執行的SQl語句為:
1 Hibernate: insert into courses (name, id) values (?, ?) 2 Hibernate: insert into students (name, id) values (?, ?) 3 Hibernate: insert into courses (name, id) values (?, ?)
(3)設一方的inverse為true,正常插入數據時輸出的SQL語句為:
1 Hibernate: insert into courses (name, id) values (?, ?) 2 Hibernate: insert into students (name, id) values (?, ?) 3 Hibernate: insert into courses (name, id) values (?, ?) 4 Hibernate: insert into students_courses (student_id, course_id) values (?, ?) 5 Hibernate: insert into students_courses (student_id, course_id) values (?, ?)
刪除學生(Student)記錄:
1 Student s=(Student)session.get(Student.class, 2L); 2 session.delete(s);
注意:
(1)如果不是Student維持關聯關系:
——若連接表students_courses中有參照students表中該記錄的記錄(即在students_courses表中存在student_id為2L的記錄)時,則刪除失敗。
——若連接表students_courses中沒有參照students表中該記錄的記錄時,則可以成功地將該記錄刪除。
(2)如果是Student維持關聯關系:
——先將連接表students_courses中參照students表中該記錄的記錄刪除,然後將該學生記錄從students表中刪除
查詢某學生選的所有課程:
1 Student s=(Student)session.get(Student.class, 2L); 2 Set<Course> set=s.getCourses(); 3 4 for (Iterator iterator = set.iterator(); iterator.hasNext();) { 5 Course course = (Course) iterator.next(); 6 System.out.println(course.getName());
某學生又選了一門新課(增加了連接表中的一條記錄):
1 Student s=(Student)session.get(Student.class, 2L); 2 Course c=(Course)session.get(Course.class,1L ); 3 s.getCourses().add(c); 4 c.getStudents().add(s);
刪除某學生的一條選課記錄(刪除了連接表中的一條記錄):
1 Student s=(Student)session.get(Student.class, 2L); 2 Course c=(Course)session.get(Course.class,1L ); 3 s.getCourses().remove(c);
(此時只會刪除連接表中的一條記錄而不會去修改Students和courses表中的記錄)
注意:雙向多對多,還可以拆成兩個多對一
1 <many-to-one name="stu" class="Student"> 2 <column name="stuId"></column> 3 </many-to-one> 4 <many-to-one name="cou" class="Course"> 5 <column name="couid"></column> 6 </many-to-one>
當遇到困難、 問題其實是好的, 只在懂不懂得努力去解決。
歡迎、感謝 您來指點我:http://www.cnblogs.com/smbk/