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

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

微信搜索 zze_coding 或扫描 👉 二维码关注我的微信公众号获取更多资源推送:

例:一个班级可以有多个学生,而一个学生只能属于一个班级。

准备

POJO

// 班级:com.zze.bean.Class
package com.zze.bean;

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

public class Class {
    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
package com.zze.bean;

import java.util.Date;

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

    private Class clazz;

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

配置文件

<!-- com/zze/bean/Class.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.Class" table="class">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <!--
        set :
            name : ‘多’的一方在当前类中的属性名
        -->
        <set name="students">
            <!--
            key :
                column : 外键表中的关联当前类对应表的外键名称
            -->
            <key column="cid"/>
            <!--
            one-to-many : 标识一对多关系
                class : ‘多’的一方全限定类名
            -->
            <one-to-many class="com.zze.bean.Student"/>
        </set>
    </class>
</hibernate-mapping>
<!-- 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 : ‘一’的一方在当前类中属性名
            column : 外键名称
            class : ‘一’的一方的全限定类名
        -->
        <many-to-one name="clazz" column="cid" class="com.zze.bean.Class"/>
    </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>
    </session-factory>
</hibernate-configuration>

使用

普通操作

例 1:两方关联,保存两方 - 新建一个班级,新建两个学生,班级主动关联学生,学生也主动关联班级,保存班级,再保存学生,成功。

Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
// 创建一个班级
Class clazz = new Class();
clazz.setName("1班");
// 创建两个学生
Student student1 = new Student();
student1.setName("张三");
Student student2 = new Student();
student2.setName("李四");

// 让班级关联学生
clazz.getStudents().add(student1);
clazz.getStudents().add(student2);

// 让学生关联班级
student1.setClazz(clazz);
student2.setClazz(clazz);
// 保存班级
session.save(clazz);

// 保存学生
session.save(student1);
session.save(student2);
transaction.commit();

image.png

例 2:两方关联,保存一方 - 新建一个班级,新建两个学生,班级主动关联学生,学生也主动关联班级,只保存班级,异常,需使用下面的级联保存。

Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
// 创建一个班级
Class clazz = new Class();
clazz.setName("1班");
// 创建两个学生
Student student1 = new Student();
student1.setName("张三");
Student student2 = new Student();
student2.setName("李四");

// 让班级关联学生
clazz.getStudents().add(student1);
clazz.getStudents().add(student2);

// 让学生关联班级
student1.setClazz(clazz);
student2.setClazz(clazz);
// 保存班级
session.save(clazz);
transaction.commit();

/*
 抛异常:org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.zze.bean.Student
 原因是:保存班级时,班级关联的学生为瞬时态对象,不能与瞬时态对象建立关系。
 */

级联操作

级联指的是操作一个对象的时候,是否会同时操作其关联的对象。
级联的方向性:

  • 操作一的一方的时候,是否会同时操作到多的一方。
  • 操作多的一方的时候,是否会同时操作到一的一方。

级联保存或更新

例 3:‘一’方关联,保存‘一’方 - 新建一个班级,新建两个学生,班级主动关联学生,只保存班级,级联保存了学生,成功。

<!-- com/zze/bean/Class.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.Class" table="class">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <set name="students" cascade="save-update">
            <key column="cid"/>
            <one-to-many class="com.zze.bean.Student"/>
        </set>
    </class>
</hibernate-mapping>
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
// 创建一个班级
Class clazz = new Class();
clazz.setName("1班");
// 创建两个学生
Student student1 = new Student();
student1.setName("张三");
Student student2 = new Student();
student2.setName("李四");

// 让班级关联学生
clazz.getStudents().add(student1);
clazz.getStudents().add(student2);
// 保存班级级联保存学生
session.save(clazz);
transaction.commit();

image.png

例 4:‘多’方关联,保存‘多’方 - 新建一个班级,新建两个学生,学生主动关联班级,只保存学生,级联保存了班级,成功。

<!-- com/zze/bean/Student.hbm.xml:修改‘多’(学生)的映射文件,在 many-to-one 标签中添加属性 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"/>
    </class>
</hibernate-mapping>
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
// 创建一个班级
Class clazz = new Class();
clazz.setName("1班");
// 创建两个学生
Student student1 = new Student();
student1.setName("张三");
Student student2 = new Student();
student2.setName("李四");

// 让学生关联班级
student1.setClazz(clazz);
student2.setClazz(clazz);
// 保存学生级联保存班级
session.save(student1);
session.save(student2);
transaction.commit();

image.png

级联删除

假如表中已有如下数据:

image.png

例 5:删除‘一’级联删除‘多’ - 删除班级,级联删除该班级下所有学生。

<!-- com/zze/bean/Class.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.Class" table="class">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <set name="students" cascade="save-update,delete">
            <key column="cid"/>
            <one-to-many class="com.zze.bean.Student" />
        </set>
    </class>
</hibernate-mapping>
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Class clazz = session.get(Class.class, 1);
session.delete(clazz);
transaction.commit();

image.png

例 6:删除‘多’级联删除‘一’ - 删除学生,级联删除该学生所属班级。

<!-- com/zze/bean/Student.hbm.xml:修改‘多’(学生)的映射文件,在 many-to-one 标签中添加属性 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"/>
    </class>
</hibernate-mapping>
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = session.get(Student.class, 1);
session.delete(student);
transaction.commit();
// 级联删除‘一’的一方,将其它关联‘一’的一方的数据的外键设为 null。
// 如果‘一’的一方也设置了级联删除,那么所有关联‘一’的一方的数据都会被删除。

image.png

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.zze.xyz/archives/hibernate4.html

Buy me a cup of coffee ☕.