使用Hibernate储存一对多对象关系

生活中的对象实体之间往往存在关系,有一对一、一对多、多对多的关系,比如一个课程班级里有多个学生就是一对多的关系。在数据库中可以通过给学生表添加外键指向班级id的方式来表示学生包含多个学生的关系,对应的在Java中,可以创建Course、Student两个类来表示班级和学生,那么如何建立二者之间的关系呢?

如下所示为数据库中的courses表和students表

   

一对多映射

第一种方式是一对多的映射,例如建立从班级(“一”)-->学生(“多”)的映射。通过可以在Course类添加一个Set集合用于保存多个Student对象,并且在hibernate中设置Course指向Student。

在IDEA中分别为两个类添加Hibernate数据库映射,会自动生成CourseEntity类与StudentEntity类文件和对应的hbm.xml映射文件,并且在hibernate.cfg.xml文件中完成注册。之后在CourseEntity中手动添加Set属性students和其get/set方法

public class CoursesEntity {
    private int id;
    private String name;
    private Integer hours;
    private Set<StudentEntity> students=new HashSet<StudentEntity>();
    
    ......

    public Set<StudentEntity> getStudents() {
        return students;
    }

    public void setStudents(Set<StudentEntity> students) {
        this.students = students;
    }

接着需要修改CourseEntity.hbm.xml文件,在其中添加set属性students,指定其对应的数据表和与本数据表courses相关联的外键,还有该属性所对应的Java实体类

    <class name="entity.CoursesEntity" table="courses" schema="test">
        <id name="id" column="id"/>
        <property name="name" column="name"/>
        <property name="hours" column="hours"/>
        <set name="students" table="students">              <!--配置students集合对应的表-->
            <key column="course_id"></key>                  <!--表中对应的外键-->
            <one-to-many class="entity.StudentEntity"/>     <!--对应的Java类-->
        </set>
    </class>

之后在测试方法中创建一个course课程对象,和两个学生对象s1、s2,并在课程的students集合中调用Set的add()添加两个学生对象,最后将这些对象都保存在数据库中。注意这里依然使用的是Junit测试单元,所以关于session和factory等的创建和关闭都在之前的setup和tearDown中完成了。

    @Test
    void testCourse(){
        //创建课程和学生对象
        CoursesEntity course=new CoursesEntity(2,"Data Structure",72);
        StudentEntity s1=new StudentEntity(1003,"小明",15);
        StudentEntity s2=new StudentEntity(1004,"小花",14);
        //在课程的students的Set集合中添加两个学生对象
        course.getStudents().add(s1);
        course.getStudents().add(s2);
        //保存课程和学生对象到数据库
        session.save(course);
        session.save(s1);
        session.save(s2);
    }

查看数据库可见course表中已经添加了对应的课程,而且students表中不仅添加了两个学生,而且其外键course_id都指向了对应的课程id

   

查看数据也很方便,可以通过course返回的Set集合遍历其中的选课学生,而hibernate会自动去students表中取出对应的学生信息

     @Test
    void findCourse() {
        CoursesEntity course = session.load(CoursesEntity.class, 2);
        System.out.println("课程名称:"+course.getName());
        //通过course返回学生Set集合遍历学生信息
        Set<StudentEntity> students = course.getStudents();
        System.out.println("课程学生:");
        for (StudentEntity student : students)
            System.out.print(student.getId() + ':' + student.getName());
    }

从课程中删除一个学生通过调用Set的remove()方法,数据库会自动修改学生course_id的外键为null

CoursesEntity course = session.load(CoursesEntity.class, 2);
Set<StudentEntity> students = course.getStudents();            //获取课程学生的Set集合
StudentEntity s=session.load(StudentEntity.class,1003);        //获取指定学生对象
//从选课列表删除指定学生
students.remove(s);         
session.save(s);        //保存操作

双向映射

除了配置Course到Student的映射,反过来也可以配置Student到Course的映射。这样就实现了两个对象类型之间的双向映射。如下进行Student --> Course的映射:

首先在StudentEntity类中添加表示课程的属性的变量course和get/set方法

public class StudentEntity {
    private int id;
    private String name;
    private Integer age;
    private Address address;
    private CoursesEntity course;    //添加表示所在课程的属性
    
    ......

    public CoursesEntity getCourse() {
        return course;
    }

    public void setCourse(CoursesEntity course) {
        this.course = course;
    }

接着配置”多“的hbm.xml文件,name为变量名,class为指向的类,column为数据表中的外键

    <class name="entity.StudentEntity" table="students" schema="test">
        <id name="id" column="id"/>
        <property name="name" column="Name"/>
        <property name="age" column="Age"/>
        <component name="address" class="entity.Address">
            <property name="city" column="city"/>
            <property name="street" column="street"/>
        </component>
        <!--配置Student指向Course的映射-->
        <many-to-one name="course" class="entity.CoursesEntity" column="course_id"/>
    </class>

接下来在代码中创建Course与Student对象并进行双向映射

    @Test
    void testCourse() {
        //创建课程和学生对象
        CoursesEntity course = new CoursesEntity(2, "Data Structure", 72);
        StudentEntity s1 = new StudentEntity(1003, "小明", 15);
        StudentEntity s2 = new StudentEntity(1004, "小花", 14);

        //添加Course --> Student映射
        course.getStudents().add(s1);
        course.getStudents().add(s2);
        //添加Student --> Course映射
        s1.setCourse(course);
        s2.setCourse(course);

        //保存课程和学生对象到数据库
        session.save(course);
        session.save(s1);
        session.save(s2);
    }

维护双向映射:通过以上操作就实现了数据表的双向映射,但是查看HIbernate执行语句如下所示,在进行了students表的两次insert操作之后又进行了两次update操作。这是由于在Course在维护一对多关系时执行力insert操作,之后student在维护多对一关系时又进行了update操作,而这样的操作是没有必要且影响执行效率。很明显这是由于双方都在维护一对多关系而造成的,因此我们希望仅由一方维护一对多关系,在Course的配置文件中设置set的inverse属性为false,代表关系由“多”的那一方来维护。

Hibernate: insert into courses (name, hours, id) values (?, ?, ?)
Hibernate: insert into students (Name, Age, city, street, course_id, id) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into students (Name, Age, city, street, course_id, id) values (?, ?, ?, ?, ?, ?)
Hibernate: update students set course_id=? where id=?
Hibernate: update students set course_id=? where id=?

级联操作:在通过session保存对象时,我们不仅执行了session.save(course),还又执行了session.save(s1)来保存学生,Course对象里已经包含了Student,应该自动一起进行保存,而不是我们手动再保存,这就需要级联操作来设置。可以对Course配置文件中的set进行配置,设置cascade属性如下

如下为设置inverse和cascade:

    <class name="entity.CoursesEntity" table="courses" schema="test">
        <id name="id" column="id"/>
        <property name="name" column="name"/>
        <property name="hours" column="hours"/>
        <!--对inverse、cascade属性进行设置-->
        <set name="students" table="students" inverse="false" cascade="save-update">
            <key column="course_id"></key> 
            <one-to-many class="entity.StudentEntity"/>
        </set>
    </class>
发布了124 篇原创文章 · 获赞 65 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/theVicTory/article/details/104542484