侧边栏壁纸
博主头像
张种恩的技术小栈博主等级

绿泡泡:___zze,添加备注来意

  • 累计撰写 748 篇文章
  • 累计创建 65 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

Hibernate(6)之多对多关系操作案例

zze
zze
2017-07-20 / 0 评论 / 0 点赞 / 786 阅读 / 14022 字

例:一名学生可以选多门课程,一门课程也可以被多名学生选择。

准备

POJO

// 学生:com.zze.bean.Student
package com.zze.bean;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private Date birthday;
    private String address;

    private Class clazz;

    private Set<Course> courses = new HashSet<>();

    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 Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public Set<Course> getCourses() {
        return courses;
    }

    public void setCourses(Set<Course> courses) {
        this.courses = courses;
    }
}
// 课程:com.zze.bean.Course  
package com.zze.bean;

import java.util.HashSet;
import java.util.Set;

public class Course {
    private Integer id;
    private String name;

    private Set<Student> students = new HashSet<>();

    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;
    }
}

配置文件

<!-- com/zze/bean/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>
    <class name="com.zze.bean.Student" table="student">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <property name="age"/>
        <property name="birthday"/>
        <property name="address"/>
        <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>

        <set name="courses" table="student_course">
            <!--
            key :
                column : 当前对象类对应中间表的外键名称
            -->
            <key column="studentId"/>
            <!--
            many-to-many : 标识多对多关系
                class : 对方类全路径
                column : 对方对应中间表的外键名称
            -->
            <many-to-many class="com.zze.bean.Course" column="courseId"/>
        </set>
    </class>
</hibernate-mapping>
<!-- com/zze/bean/Course.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>
    <class name="com.zze.bean.Course" table="course">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <set name="students" table="student_course">
            <!--
            key :
                column : 当前对象类对应中间表的外键名称
            -->
            <key column="courseId"/>
            <!--
            many-to-many : 标识多对多关系
                class : 对方类全路径
                column : 对方对应中间表的外键名称
            -->
            <many-to-many class="com.zze.bean.Student" column="studentId"/>
        </set>
    </class>
</hibernate-mapping>
<!-- 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="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://192.168.208.153:3306/test</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="current_session_context_class">thread</property>
        <property name="hibernate.hbm2ddl.auto">create</property>
        <mapping resource="com/zze/bean/Class.hbm.xml"></mapping>
        <mapping resource="com/zze/bean/Student.hbm.xml"></mapping>
        <mapping resource="com/zze/bean/Course.hbm.xml"></mapping>
    </session-factory>
</hibernate-configuration>

使用

普通操作

例:两方关联,保存两方 - 新建一个学生,新建一个课程,让学生主动关联课程,也让课程主动关联学生,保存学生与课程,异常。

Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setName("张三");
Course course = new Course();
course.setName("数学");
student.getCourses().add(course);
course.getStudents().add(student);
session.save(student);
session.save(course);
transaction.commit();
/*
 抛异常:org.hibernate.exception.ConstraintViolationException: could not execute statement
 */

抛异常的原因是:多对多关系的维护是依赖第三张中间表,而中间表的字段是两个分别关联多对多这两张表的主键的外键,且这两个外键在中间表中组成了联合主键,而上述代码实际上进行了两次 insert 操作,这两次操作的内容违反了联合主键约束,所以会抛出异常。

分析原因后,可以得出结论,在多对多中两方关联时,必须有一方得放弃维护外键关系(一般是被动方放弃,即 Course 放弃),下面配置二选一:

<!-- com/zze/bean/Student.hbm.xml:修改 set 标签,添加属性 inverse="true" -->
<?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>
    <class name="com.zze.bean.Student" table="student">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <property name="age"/>
        <property name="birthday"/>
        <property name="address"/>
        <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>

        <set name="courses" table="student_course" inverse="true">
            <!--
            key :
                column : 当前对象类对应中间表的外键名称
            -->
            <key column="studentId"/>
            <!--
            many-to-many : 标识多对多关系
                class : 对方类全路径
                column : 对方对应中间表的外键名称
            -->
            <many-to-many class="com.zze.bean.Course" column="courseId"/>
        </set>
    </class>
</hibernate-mapping>
<!-- com/zze/bean/Course.hbm.xml:修改 set 标签,添加属性 inverse="true" -->
<?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>
    <class name="com.zze.bean.Course" table="course">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <set name="students" table="student_course" inverse="true">
            <!--
            key :
                column : 当前对象类对应中间表的外键名称
            -->
            <key column="courseId"/>
            <!--
            many-to-many : 标识多对多关系
                class : 对方类全路径
                column : 对方对应中间表的外键名称
            -->
            <many-to-many class="com.zze.bean.Student" column="studentId"/>
        </set>
    </class>
</hibernate-mapping>

修改配置后,再次执行上述示例代码,成功。

级联操作

级联保存或更新

例:一方关联,保存一方 - 新建一个学生,新建一个课程,让学生主动关联课程,保存学生,级联保存了课程,成功

<!-- com/zze/bean/Student.hbm.xml:修改 set 标签,添加属性 cascade="save-update" -->
<?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>
    <class name="com.zze.bean.Student" table="student">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <property name="age"/>
        <property name="birthday"/>
        <property name="address"/>
        <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>

        <set name="courses" table="student_course" cascade="save-update">
            <!--
            key :
                column : 当前对象类对应中间表的外键名称
            -->
            <key column="studentId"/>
            <!--
            many-to-many : 标识多对多关系
                class : 对方类全路径
                column : 对方对应中间表的外键名称
            -->
            <many-to-many class="com.zze.bean.Course" column="courseId"/>
        </set>
    </class>
</hibernate-mapping>
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setName("张三");
Course course = new Course();
course.setName("数学");
student.getCourses().add(course);
session.save(student);
transaction.commit();

级联删除

例:删除学生,级联删除该学生所选的课程。

<!-- com/zze/bean/Student.hbm.xml:修改 set 标签,添加属性 cascade="delete" -->
<?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>
    <class name="com.zze.bean.Student" table="student">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <property name="age"/>
        <property name="birthday"/>
        <property name="address"/>
        <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>

        <set name="courses" table="student_course" cascade="save-update,delete">
            <!--
            key :
                column : 当前对象类对应中间表的外键名称
            -->
            <key column="studentId"/>
            <!--
            many-to-many : 标识多对多关系
                class : 对方类全路径
                column : 对方对应中间表的外键名称
            -->
            <many-to-many class="com.zze.bean.Course" column="courseId"/>
        </set>
    </class>
</hibernate-mapping>
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = session.get(Student.class, 1);
session.delete(student);
transaction.commit();
0

评论区