Hibernate的双向多对多的关系映射

来源:互联网 发布:靓心阁优化门窗 编辑:程序博客网 时间:2024/06/11 14:54

      最近在学习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