最近在學習Hibernate的時候,對象關係映射(ORM)特別重要也特別難懂,現在來記錄一下學習過程.
多對多的關係在生活中特別常見,比如老師和學生.一個老師有對應多個學生,一個學生可以有好多個老師.現在就以這個爲例子進行學習.
一.首先,建表
對多對的關係映射在僅有的兩張數據庫表中student和teacher是對應不起來的.需要一箇中間表student_teacher來建立聯繫.
studen表 (student_id爲主鍵自增)
teacher表(teacher_id是主鍵自增)
中間表student_teacher—>student_id和teacher_id是聯合主鍵(不能設置自動遞增)
二.創建映射實體類
Student.java
(生成的get.set方法就不寫了)
public class Student {
private Integer studentId;
private String studentName;
private Set<Teacher> teachers = new HashSet<>();
Teacher.java
(生成的get.set方法就不寫了)
public class Teacher {
private Integer teacherId;
private String teacherName;
private Set<Student> students = new HashSet<>();
三.寫對應的映射文件
student.hbm.xml
<hibernate-mapping package="com.qiushiju.model">
<class name="Student" table="student">
<id name="studentId" column="student_id">
<generator class="native"></generator>
</id>
<property name="studentName" column="student_name" />
<set name="teachers" table="student_teacher" >
<key column="student_id" />
<many-to-many class="Teacher" column="teacher_id" />
</set>
</class>
</hibernate-mapping>
Teacher.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.qiushiju.model">
<class name="Teacher" table="teacher">
<id name="teacherId" column="teacher_id">
<generator class="native" />
</id>
<property name="teacherName" column="teacher_name" />
<set name="students" table="student_teacher" >
<key column="teacher_id"/>
<many-to-many class="Student" column="student_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
四.編寫Hibernate的配置文件.把映射文件注入配置文件中
配置文件爲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>
<!-- 數據庫的連接信息 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate2</property>
<property name="connection.username">root</property>
<property name="connection.password">qqq030053</property>
<!-- 數據庫方言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 是否打印展示SQL語句 -->
<property name="show_sql">true</property>
<!-- 當前session保存在什麼地方 -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
<!-- 注入映射文件-->
<mapping resource="M2MStudent.hbm.xml"/>
<mapping resource="M2MTeacher.hbm.xml"/>
</session-factory>
</hibernate-configuration>
五.寫測試類
先測試添加
public class TestManyToMany {
public static void main(String[] args) {
//讀取cfg.xml文件
Configuration configuration = new Configuration().configure();
//建立sessionFactory
SessionFactory factory = configuration.buildSessionFactory();
//取得session
Session session = factory.openSession();
session.beginTransaction();
//創建多個老師
Teacher teacher1 = new Teacher();
teacher1.setTeacherName("老師1");
Teacher teacher2 = new Teacher();
teacher2.setTeacherName("老師2");
//創建多個學生
Student student1 = new Student();
student1.setStudentName("學生1");
Student student2 = new Student();
student2.setStudentName("學生2");
//設定關聯關係
//爲每個老師添加多名學生
teacher1.getStudents().add(student1);
teacher1.getStudents().add(student2);
teacher2.getStudents().add(student1);
teacher2.getStudents().add(student2);
//爲每個學生配多名老師
student1.getTeachers().add(teacher1);
student1.getTeachers().add(teacher2);
student2.getTeachers().add(teacher1);
student2.getTeachers().add(teacher2);
//保存
session.save(teacher1);
session.save(teacher2);
session.save(student1);
session.save(student2);
// 提交.-->這一步纔是把數據真正提交至數據庫中
session.getTransaction().commit();
輸出結果爲:
異常:
ERROR: Duplicate entry ‘9-9’ for key ‘PRIMARY’
Exception in thread “main” org.hibernate.exception.ConstraintViolationException: could not execute statement
出現異常是因爲兩個映射文件的inverse都設爲默認的false,則會出現(主鍵重複),導致插入失敗;如果兩邊都設置inverse爲true,則Student和Teacher都去維護關聯關係,即同時向連接表插入記錄,則會導致主鍵重複插入失敗.
解決辦法:讓其中一方的inverse設爲true,讓對方去維護關聯關係.
[PS:inverse屬性:nverse的真正作用就是指定由哪一方來維護之間的關聯關係。當一方中指定了“inverse=false”(默認),那麼那一方就有責任負責之間的關聯關係,設爲true就是放棄維護關聯關係.讓對方維護;cascade屬性:級聯操作]
比如Teacher.hbm.xml中修改爲:
<set name="students" table="student_teacher" cascade="all" inverse="true">
修改後輸出結果爲:
Hibernate: insert into teacher (teacher_name) values (?)
Hibernate: insert into student (student_name) values (?)
Hibernate: insert into teacher (teacher_name) values (?)
Hibernate: insert into student (student_name) values (?)
Hibernate: insert into student_teacher (student_id, teacher_id) values (?, ?)
Hibernate: insert into student_teacher (student_id, teacher_id) values (?, ?)
Hibernate: insert into student_teacher (student_id, teacher_id) values (?, ?)
Hibernate: insert into student_teacher (student_id, teacher_id) values (?, ?)
此時數據庫爲:
teacher
student_teacher
student
六.測試關聯查詢
//通過session操作數據庫,得到老師對象
Teacher teacher3 = (Teacher) session.get(Teacher.class, 7);
//老師信息爲
System.out.println("--->"+teacher3.getTeacherName());
//通過老師找到這個老師對應的全部學生集合
Set<Student> students = teacher3.getStudents();
//遍歷輸出學生
for (Student student : students) {
System.out.println(student.getStudentName());
}
輸出結果爲:
--->老師1
學生2
學生1