Hibernate一对多映射_慕课网笔记

本文详细介绍了Hibernate中的一对多映射关系,包括单向一对多、单向多对一和双向多对一的配置与测试。通过实例解析了如何在数据库中通过主外键实现关联,以及在Java中通过实体类和映射文件实现一对多关系。同时,探讨了cascade和inverse属性在一对多映射中的作用和影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 简介

Hibernate的映射类型(最常用为一对多和多对一映射): 

一对多 (one-to-many) 

多对一 (many-to-one)

一对一 (one-to-one)

多对多 (many-to-many)

二、单向一对多关联
1. 定义

就像班级和学生。站在班级的角度看。班级和学生就是一对多的关系,班级为一方,学生为多方且是单向的。

如何实现这种关系?
1.在数据库中,可以通过添加主外键的关联,表现一对多的关系;
2.在java中,通过在一方持有多方的集合实现,即在“一”的一端中使用<set>元素表示持有“多”的一端的对象

2. 以班级和学生关联为例

(1) 创建工程,导入hibernate和数据库驱动相关jar包
(2) 配置hibernate.cfg.xml文件
(3) 创建HibernateUtil工具类,用以获取会话工厂,会话的创建和关闭
Tips:不同的hibernate版本,会话的获取方式不一样,需要注意

package com.imooc.util;
import com.imooc.entity.Grade;
import com.imooc.entity.Student;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateUtil {
    private static final SessionFactory sessionFactory = buildSessionFactory();
    private static SessionFactory buildSessionFactory() {
        Configuration config = new Configuration().configure(); // 创建配置对象
        config.addClass(Grade.class);
        config.addClass(Student.class);
        ServiceRegistry serviceRegistery = new StandardServiceRegistryBuilder().applySettings(config.getProperties()).build();
        return config.buildSessionFactory(serviceRegistery);

    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static Session getSession() {return sessionFactory.openSession();}

    //关闭Session对象
    public static void closeSession(Session session){
        if(session!=null){
            session.close();
            sessionFactory.close();
        }
    }
}

(4) MySQL数据库中建表

create table grade
(
	gid int primary key,
	gname varchar(20) not null,
	gdesc varchar(50) 
);
create table student
(
	sid int primary key,
	sname varchar(20) not null,
	gender varchar(2) ,
	gid int
);
alter table student add constraint fk_student_gid foreign key (gid) 
references grade(gid);//对应表主键

Tips: Intellij IDEA集成的hibernate会自行建表,这一步可省略;但是根据实体类自动建表的话,就必须在Student实体类中定义Grade grade属性,否则因为建立关联时Student数据表中找不到gid这一项,会报错

(5) 创建持久化类和映射文件

Grade.java

package com.imooc.entity;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

public class Grade implements Serializable{
    private int gid;
    private String gname;
    private String gdesp;
    private Set<Student> students = new HashSet<>(); //一方持有多方的集合

    /**getter和setter方法**/
    {...}
    public Grade() {
    }

    public Grade(String gname, String gdesp) {
        this.gname = gname;
        this.gdesp = gdesp;
    }

    public Grade(int gid, String gname, String gdesp, Set<Student> students) {
        this.gid = gid;
        this.gname = gname;
        this.gdesp = gdesp;
        this.students = students;
    }
}

Student.java

package com.imooc.entity;

import java.io.Serializable;

public class Student implements Serializable {
    private int sid;
    private String sname;
    private String gender;
    //如果选择hibernate自动创建数据库表,这个属性不能漏掉
    private Grade grade;

    public Student(int sid, String sname, String gender) {
        this.sid = sid;
        this.sname = sname;
        this.gender = gender;
    }

    public Student() {
    }

    public Student(String sname, String gender) {
        this.sname = sname;
        this.gender = gender;
    }
}

Grade.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.imooc.entity.Grade" table="grade">
        <id name="gid" type="int">
            <column name="gid" />
            <generator class="increment" />
        </id>
        <property name="gname" type="java.lang.String">
            <column name="gname" />
        </property>
        <property name="gdesp" type="java.lang.String">
            <column name="gdesp" />
        </property>
        <set name="students" table="student">
            <key column="gid"></key>
            <one-to-many class="com.imooc.entity.Student" />
        </set>
    </class>
</hibernate-mapping>

Tips:<set>中key指定的是grade(被参照表)的主键,one-to-many指定了Set中元素(参照元素)的类型

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.imooc.entity.Student" table="student">
        <id name="sid" type="int">
            <column name="sid" />
            <generator class="increment" />
        </id>
        <property name="sname" type="java.lang.String">
            <column name="sname" length="20" not-null="true"/>
        </property>
        <property name="gender" type="java.lang.String">
            <column name="gender" />
        </property>
    </class>
</hibernate-mapping>

Tips: 最后需要在hibernate.cfg.xml中配置<mapping resource>

(6) 测试-增删改查

package com.imooc.entity;

import com.imooc.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import sun.security.ssl.HandshakeInStream;

import java.util.Set;

/**
 * 单向一对多关联关系,班级(一方) --- > 学生(多方)
 * 建立关联关系后,可以方便地从一个对象导航到另一个对象
 * 注意关联的方向
 */
public class Test {
    public static void main(String[] args) {
        //add();
        //getStudentsByGrade();
        //update();
        delete();
    }

    //将学生添加到班级
    public static void add() {
        Grade g = new Grade("生物一班", "班主任:钱**");
        Student stu1 = new Student("李明", "男");
        Student stu2 = new Student("刘丹", "女");

        //如果希望在学生表中添加对应的班级编号,需要在班级中添加学生,建立关联关系
        g.getStudents().add(stu1);
        g.getStudents().add(stu2);

        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        session.save(g);
        session.save(stu1);
        session.save(stu2);
        tx.commit();
        HibernateUtil.closeSession(session);
    }

    //通过班级获取学生
    public static void getStudentsByGrade(){
        Session session = HibernateUtil.getSession();
        Grade grade = (Grade) session.get(Grade.class, 1);
        System.out.println(grade.getGname() + " " + grade.getGdesp());
        Set<Student> students = grade.getStudents();
        for (Student stu :students) {
            System.out.println(stu.getSname() + " " + stu.getGender());
        }
        HibernateUtil.closeSession(session);
    }

    //修改学生信息
    public static void update() {
        Grade g = new Grade("生物二班", "班主任:王**");
        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        Student stu = session.get(Student.class, 1);
        g.getStudents().add(stu);
        session.save(g);
        tx.commit();
        HibernateUtil.closeSession(session);
    }

    //删除学生信息
    public static void delete() {
        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        Student stu = (Student) session.get(Student.class, 2);
        session.delete(stu);
        tx.commit();
        HibernateUtil.closeSession(session);
    }
}

执行输出日志如下:

Hibernate: alter table student add constraint FKrahcsicxucn7bhee8empkb42c foreign key (gid) references grade (gid)
Hibernate: select max(gid) from grade
Hibernate: select max(sid) from student
Hibernate: insert into grade (gname, gdesp, gid) values (?, ?, ?)
Hibernate: insert into student (sname, gender, sid) values (?, ?, ?)
Hibernate: insert into student (sname, gender, sid) values (?, ?, ?)
Hibernate: update student set gid=? where sid=?
Hibernate: update student set gid=? where sid=?

Tips:1.出现数据表插入中文为乱码的情况时,可以简单地修改数据库编码方式为utf8:LTER DATABASE hibernateTest CHARACTER SET utf8;或者修改MySQL默认字符集为utf8;同时hibernate.cfg.xml的connection.url配置中需要设置编码方式jdbc:mysql://127.0.0.1:3306/hibernateTest?useUnicode=true&amp;characterEncoding=utf8

2.在"一"保存"多"的集合创建一对多关系:

a.在插入"一"的数据时,首先将"一"的元素(除了建立一对多关系的集合)添加到对应表项,然后根据集合元素到"多"对应的表中更新"多"的参照数据(集合只用于更新"多"的数据,而不会作为"一"的数据保存)
b.可以直接(不用二次查表)通过"一"得到相应的"多",反之不行(导航查询要注意方向:一到多)

(7) set元素的常用属性

属性含义和作用是否必须默认值
name映射类属性的名称Y
table关联类的目标数据库N
lazy指定关联对象是否使用延迟加载Nproxy
inverse表示双向关联中被动的一方Nfalse

三、单向多对一关联
1. 简介
1.多对一的关系和关系数据库中的外键参照关系最匹配,即在己方的表中的一个外键参照另一个表的主键;以班级和学生为例,从学生的角度看,学生就是多方,班级是一方
2.通过在多方持有一方的引用实现,需要在“多”的一端(此处为学生端)使用<many-to-one>配置

2. 配置
首先在Student类中增加属性 private Grage grade,然后配置Student.hbm.xml中的多对一关联如下,并删除Grage.hbm.xml中的一对多关联配置,构成纯粹的单向多对一映射。
<!-- 配置多对一关联关系-->
        <many-to-one name="grade" class="com.imooc.entity.Grade" column="gid"/>
3. 测试单向多对一
package com.imooc.entity;

import com.imooc.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 * 单向多对一 (学生--->班级)
 */
public class Test02 {
    public static void main(String[] args) {
        save();
    }

    public static void save() {
        Grade grade = new Grade("化学一班", "班主任:林**");
        Student stu1 = new Student("任盈盈", "女");
        Student stu2 = new Student("令狐冲", "男");
        stu1.setGrade(grade);
        stu2.setGrade(grade);

        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        session.save(grade);
        session.save(stu1);
        session.save(stu2);
        tx.commit();
        HibernateUtil.closeSession(session);
    }
}
执行输出日志如下,注意没有执行update student set gid=? where sid=?,因为在保存学生的时候已经设置了其gid
Hibernate: alter table student add constraint FKrahcsicxucn7bhee8empkb42c foreign key (gid) references grade (gid)
Hibernate: select max(gid) from grade
Hibernate: select max(sid) from student
Hibernate: insert into grade (gname, gdesp, gid) values (?, ?, ?)
Hibernate: insert into student (sname, gender, gid, sid) values (?, ?, ?, ?)
Hibernate: insert into student (sname, gender, gid, sid) values (?, ?, ?, ?)
四、双向多对一关联
配置了双向关系后,可以方便地从班级查询到其包含的学生信息,也可以从学生查询到其所属的班级信息
1. 修改实体类及hbm文件配置

a. 分别在持久化类中定义另一方的引用;

 一方:private Set<Student>students=new HashSet<Student>(),并setter、getter

 多方:private Grade grade,并setter、getter;

b. 在一方hbm文件配置一对多关联,在多方配置多对一关联关系

方配置:

 <set name="一方实体类定义的多方的引用,即多方属性Students" table="多方的表名">

  <key column="关联外键的列gid"></key>

  <one-to-many class="多方实体类的全类名com.imooc.entity.Student" ></one>

 </set>

 多方配置:

 <many-to-one name="指定对应的属性的名称" class="对应实体类的全类名com.imooc.entity.Grade" column="指定 外键的列名gid" ></many-to-one>

2. 测试
public static void save() {
        Grade grade = new Grade("化学一班", "班主任:林**");
        Student stu1 = new Student("任盈盈", "女");
        Student stu2 = new Student("令狐冲", "男");
        //设置关联关系
        grade.getStudents().add(stu1);
        grade.getStudents().add(stu2);
        stu1.setGrade(grade);
        stu2.setGrade(grade);

        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        session.save(grade);
        session.save(stu1);
        session.save(stu2);
        tx.commit();
        HibernateUtil.closeSession(session);
    }
执行输出日志如下,可以看到student学生表中插入数据时已经设置了gid,后面又调用了update更新gid(这是Grade表在进行维护操作),update操作在这边没有必要,影响性能,可以通过将“一方”hbm文件的<set>属性设置为inverse="true"来改善性能
Hibernate: alter table student add constraint FKrahcsicxucn7bhee8empkb42c foreign key (gid) references grade (gid)
Hibernate: select max(gid) from grade
Hibernate: select max(sid) from student
Hibernate: insert into grade (gname, gdesp, gid) values (?, ?, ?)
Hibernate: insert into student (sname, gender, gid, sid) values (?, ?, ?, ?)
Hibernate: insert into student (sname, gender, gid, sid) values (?, ?, ?, ?)
Hibernate: update student set gid=? where sid=?
Hibernate: update student set gid=? where sid=?
3. inverse属性

1.<set>节点的inverse属性指定关联关系的控制方向,默认由one方来维护;

2.关联关系中,inverse="false"(默认值)则为主动方,由主动方负责维护关联关系;

3.在一对多关联中,只能设置one方的inverse为true,这将有助于性能的改善。

4. cascade属性:

1、当设置了cascade属性不为none时,Hibernate会自动持久化所关联的对象;

2、cascade属性的设置会带来性能上的变动,需要谨慎设置;

属性值含义和作用
all对所有操作进行级联操作
save-update执行保存和更新操作时进行级联操作
delete执行删除时进行级联操作
none所有操作都不执行级联操作
Tips:1.在Grade的映射文件中的<set>节点添加cascade属性为"save-update",那么在test中向Grade添加Student后,只需保存Grade对象,可以不用显式保存Student对象

2.在Student的映射文件中的<many-to-one>节点添加cascade属性为"save-update",那么在设置Student对象的Grade属性后,保存Student对象时,会级联操作,保存Grade对象

五、总结

实现单向一对多:

 1)在one方的实体中添加保存many方的集合

 2)在one方的配置文件中添加<one-to-many>配置

实现单向多对一:

 1)在many方的实体中添加one方的引用

 2)在many方的配置文件中添加<many-to-one>配置

常用属性:

cascade:设置级联关系

 all:对所有操作进行级联操作

 save-update:执行保存和更新操作时进行级联操作

 delete:执行删除操作时进行级联操作

 none:对所有操作不进行级联操作】

inverse:设置由哪一方维护关联关系;inverse="false"则为主动方,由主动方负责维护关联关系


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值