Hibernate入门七(关联对象)

本文深入探讨了一对多、多对一及多对多等关联关系的ORM映射方式,包括不同场景下的关联维护策略和效率对比,以及一对一关联的具体应用场景。

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

关联关系映射速览

这里写图片描述

电脑本地链接

一对多的双向关联

这里写图片描述
一般企业中很少使用删除,只是状态的变化。
inverse针对的外键
首先呢 classes这个类不用更改
一个学生对于一个班级,所以在Student中添加关联对象,实现set和get方法

在学生的映射文件中,有个其属性中,有column代表外键 class写上对应的类,这个外键指的是我通过classes 联系的student,说白了,给我一个班级,我可以得到学生。
而另一个外键指的是给我学生的姓名,我可以找班级,所以说在两个表的映射文件中,都有设置外键。
表结构不变,这样的话我就可以在hibernate客户端通过学生来操作班级了。

我们来看一下哪种效率比较高

这通过学生来建立的关联,我去找studengt的映射文件,设置cascade ,这样的话

public class OneToManyTest extends HibernateUtils{
    /**
     * Hibernate: 
     //保存一个学生 因为学生主键的生成需要查最大主键
            select  max(sid)   from     Student
        Hibernate:      
     //保存班级信息 查询主键的最大值
            select  max(cid)   from     Classes
        Hibernate: 
            insert  into  Classes   (description, name, cid)    values    (?, ?, ?)
        Hibernate: 
       //cid 是外键 直接插入进去了,如果是 classes级联的时候 先保存学生的一般属性 再发出条update语句更新主键值
        外键直接插入进去了,无需发出update语句
            insert   into Student  (description, name, cid, sid) values  (?, ?, ?, ?)

     */
    @Test
    public void testSaveStudent_Cascade_Save_Classes(){
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        Student student = new Student();
        student.setName("asfd");

        Classes classes = new Classes();
        classes.setName("12期");

        //创建班级与学生之间的关联
        student.setClasses(classes);  //通过学生建立的关联

        session.save(student);
        transaction.commit();
        session.close();
    }

    /**
     * update
            Student 
        set
            description=?,
            name=?,
            cid=? 
        where
            sid=?
     */
    @Test
    public void testTransform(){
        /**
         * 把sid为11的学生从cid为2的班级转移到cid为1的班级
         * cid为2的班级需要查出来吗?我最终要通过student来操作,我可以setclasess为最新班级
         * 我只需要得到学生 
         * 
         */
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        Student student = (Student)session.get(Student.class, 11L);
        Classes classes = (Classes)session.get(Classes.class, 1L);
        //建立关系
        student.setClasses(classes);
        transaction.commit();
        session.close();
    }

    /**
     * 把一个学生从一个班级中移除
     * 这个update语句很关键 我只是想更新外键就可以 但是结果更改了除主键以外的所有属性
     * 没有inverses 
     * 我通过student 来维护关系或者说来做级联 他操作的就是学生信息本省 不存在维护的关系 因为值一直都有
     * 所有不单独发出更新的sql语句 
     *     update
                Student 
            set
                description=?,
                name=?,
                cid=? 
            where
                sid=?
     */
    @Test
    /*
    *我实际上删除一个学生 不需要知道学生在哪个班级  我直接设置班级为null发现一个事情  整个学生信息都变成空了
    */
    public void testRemoveFromClasses(){
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        Student student = (Student)session.get(Student.class, 11L);
        student.setClasses(null);
        transaction.commit();
        session.close();
    }
}

来总结一下 :

一对多的双向
  • 当多的一方维护关系时,不会发出更新关系的update语句,
  • 而一的一方维护关系时,需要维护关系的updates,
  • 一般清况下,多的一方维护关系的效率高。
    也有特殊情况,实际操作的过程看需求。

多对多的关系

最简单的多对多的关系的例子很多,比如学生和老师,学生和课程。

一个学生选修多个课程 所以需要一个set集合
在course 中,有多个学生选修一个课程,

一对多:对象和集合之间的关系,或者对象和对象之间的关系。
多对一:对象与对象之间的关系
多对多“对象和集合之间的关系”

多对对 数据库中至少3张表 第三章表有两个外键

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.itheima12.hibernate.domain.Student">
        <id name="sid" length="5">
            <generator class="increment"></generator>
        </id>
        <property name="description" length="50"></property>
        <property name="name" length="20"></property>
        <!-- 
            table
                    第三张表
         -->
        <set name="courses" table="student_course" cascade="save-update">
            <key>
                <!-- 
                    外键 对应的就是当前类的配置文件的的主键
                 --> 
                <column name="sid"></column>
            </key>
            <!-- 
                column外键
             -->
            <many-to-many class="com.itheima12.hibernate.domain.Course" column="cid"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

你得告诉第三张表 拼sql语句,这两个id就是建立关联的,
同样的course也是一样的,配置配置文件。
你只要想它怎么生成sql语句 就很好理解了
这里写图片描述
内连接的多对多的查询
多对多要分析几个问题:
谁维护关系的效率最高呢? 答案是没有区别

比如我现在有个学生要选修一门课程 那么在数据库中插入到第三张表中一条数据就好了
多对多建立关系相当于在第三张表中插入一条数据,多对多解除关系相当于在第三章表中删除的话
多对多改变一条关系,相当于先删除再增加

看需求来选择谁来维护表。
用户的岗位也是多对多 一个用户有很多个岗位。

package com.itheima12.hibernate.manytomany;

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

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.itheima12.hibernate.domain.Course;
import com.itheima12.hibernate.domain.Student;
import com.itheima12.hibernate.utils.HibernateUtils;


public class ManyToManyTest extends HibernateUtils{
    /**
     * 保存学生的时候,同时保存课程
     * Hibernate: 
            select
                max(sid) 
            from
                Student
        Hibernate: 
            select
                max(cid) 
            from
                Course
        Hibernate: 
            insert  into  Student (description, name, sid) values  (?, ?, ?)
        Hibernate: 
                  在Student.hbm.xml文件中
               <set cascade="save-update">
            insert into Course(description, name, cid) values   (?, ?, ?)
        Hibernate: 
                   建立关系的sql语句
            insert    into   student_course  (sid, cid)  values  (?, ?)

     */
    @Test
    public void testSaveStudent_Cascade_Save_Course(){
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        Student student = new Student();
        student.setName("aa");

        Course course = new Course();
        course.setName("java基础课程");

        //建立学生与课程之间的关系
        Set<Course> courses = new HashSet<Course>();
        courses.add(course);
        student.setCourses(courses);

        session.save(student);
        transaction.commit();
        session.close();
    }

    /**
     * 已经存在一个课程,已经存在一个学生,建立该课程和该学生之间的关系
     */
    @Test
    public void testBuildR(){
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        //把cid为2的课程和sid为1的学生提取出来
        Course course = (Course)session.get(Course.class, 2L);
        Student student = (Student)session.get(Student.class, 1L);
        //接下来建立关系 即便学生选修的课程为空的话 也能获取的到 hibernate在内部可以创建空集合
        student.getCourses().add(course);
        //course.getStudent().add(class);从课程出发和冲学生出发的意义是一样的
        //
        transaction.commit();
        session.close();
    }

    /**
     * 已经存在一个课程,新建一个学生,建立该课程和该学生之间的关系
     * 首先 我要创建一个学生  新的学生没有课程
     */
    @Test
    public void testSaveStudent_BuildR(){
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        //创建新学生
        Student student = new Student();
        student.setName("student1");
        //提取出课程信息 
        Course course = (Course)session.get(Course.class, 1L);
        /**
         * 建立了新的学生与该课程之间的关系 双向维护的时候 
         */
        Set<Course> courses = new HashSet<Course>();
        //首先 
        courses.add(course);
        student.setCourses(courses);
        //因为学生是临时/游离状态 所以需要save一下
        session.save(student);
        transaction.commit();
        session.close();
    }

    /**
     * 一个学生从一门课程转到另外一门课程
     */
    @Test
    public void testTranform(){
        /**
         * sid为1的学生从课程2转到课程1
         * 
         */
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        //首先 提取sid为1的学生
        Student student = (Student)session.get(Student.class, 1L);
        //能不能 Student内存在的是一个course1的集合,那么课程2中依然存在这个学生
        //把课程1和课程2提取出来
        Course course1 = (Course)session.get(Course.class, 1L);
        Course course2 = (Course)session.get(Course.class, 2L);
        //你得先干掉 不然的话都设置为null了
        student.getCourses().remove(course2);//解除sid为1的学生和cid为2的课程
        student.getCourses().add(course1);//建立sid为1的学生和cid为1的课程

        transaction.commit();
        session.close();
    }

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述
这就上面的粒子发出的sql语句,那么问题来了,这么麻烦 ,有没有优化的方法?
答案:没有!
你得用面向对象的方法来整hibernate,还好有一级缓存。
注意:如果有一个表中有千万级别的数据,那么就不能使用这个hibernate因为废了


    /**
     * 解除一个学生和该学生所学的所有的课程之间的关系
     */
    @Test
    public void testRealseAllR(){
        /**
         * 解除sid为1的学生和所有的课程之间的关系
         */
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        //先得到学生信息
        Student student = (Student)session.get(Student.class, 1L);
        //设置为空就行了
        student.setCourses(null);
        transaction.commit();
        session.close();
    }

    /**
     * 解除一个学生和所有的课程之间的关系,再建立一些课程之间的关系
     * 例如:
     * 如果:我先解除这个学生和课程的所有信息,然后再  add新的课程,那么效率就非常低了
     */
    @Test
    public void testRealseAllRAndBuildR(){
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        //首先得到这个学生信息
        Student student = (Student)session.get(Student.class, 1L);
        //整个set集合 
        Set<Course> courses = new HashSet<Course>();
        //要建立和course3的关系
        Course course = (Course)session.get(Course.class, 3L);
        courses.add(course);
        //把原来的student中的courses给覆盖掉了
        student.setCourses(courses);
        transaction.commit();
        session.close();
    }
}

一对一:
实际上再企业中使用的很少。
什么时候存在一对一?一个人一个身份证

package com.itheima12.hibernate.onetoone;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.itheima12.hibernate.domain.Classes;
import com.itheima12.hibernate.domain.Student;
import com.itheima12.hibernate.utils.HibernateUtils;


public class OneToOneTest extends HibernateUtils{
    @Test
    public void testBuildR(){
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        //把sid为12的学生,把cid为1的班级提取出来
        Student student1 = (Student)session.get(Student.class, 1L);
        Student student2 = (Student)session.get(Student.class, 2L);
        Classes classes = (Classes)session.get(Classes.class, 1L);
        //建立关系
        student1.setClasses(classes);
        student2.setClasses(classes);
        transaction.commit();
        session.close();
    }
}

龙哥上课课堂笔记:

外键一对多
现有person
再有address
这样才能进行关联

如果是使用中间表的时候
那么在address中就没有pi’d 了

通过中间表的话:可以不分先存谁 再 设置他们之间的关系。

单纯的存一个person对象没问题
将person对象屏蔽
再存一个address的话 也没有问题

之后如何建立关联呢 ?
判断对象的状态:根据持久化标识
在程序中 是不是设置了id
persion 和 address准备好
person中要求有一个集合 来建立之间的关系
set.add()_
p.setAddress(set)
尝试保存一下 person
当前 我这是new的对象
因为数据库中 已经有这个对象了,发insert语句

但是如果我设置了setid(1)

发现还是出错 因为 id=1 name =scotte 一摸一样的数据
地址也是游离态 现在save Person
1.save

2.中间表 当前的person和那个地址进行关联 说的没有id

有好多对象 都是框架给的 分不清不
单实例对象:同意一个对象 ge’t一个对象 所有人都操作同一个对象
多实例:操作不同的对象
所以 需要设置 address 的id 才能插入去

一但有了持久化标识 id 我就是托管态的对象


如果:
中间表 中加了唯一型约束:就变成一对多的关系 违反唯一性约束
如果中间表中 有何人的地址id=1 再存一个人 地址id=1 出差 报错为:有一条语句不能执行

如果 把pid=4的删除了,怎么办

先删除中间表
在删除peron

如果我删除的是1号地址而不删除person 会怎么样?
自己实验。


这个得看 会涉及到级联删除 和 控制反转


多对一

join 有个optionnal =true
通过 many-to-one 元素,可以定义一种常见的与另一个持久化类的关联。这种关系模型是多对一关联(实际上是一个对象引用-译注):这个表的一个外键引用目标表的主键字段。

基于中间表的多对一
中间表的 coulm 来 配置外键的关系

你得配置 唯一性约束 不然的话 就是多对多了

先设计表 再设计类 两张表之间通过外键建立练习
大多数情况下满足到二范式
三范式 就可以进行中间表的设计
多对多没办法 只能是中间表 为的是将来查数据库的时候 方便


单项和双向关联 :
单项:能够通过A表找到B表,通过B表找不到A表
双向关联的需求是:能够通过A表找到B表,通过B表找到A表。怎么实现呢?

我们可以知道,单存的B表数据找到的是一批A表的数据,也就是A表数据对象的集合,当然,改变的不是数据库中的表,而是持久化类。B表的持久化类中加入一个A表的Set集合,将查询到的与B有关的数据放入该集合中,之后就可以通过其他的条件得到我们想要的对应A的信息了。

总结:
单向和双向关联的区别就在于,两个类是否需要互相知道,如果类A需要知道类B,而类B也需要知道类A,那么这两个类就应该是双向关联的,而如果只需要类A知道类B,而不需要类B知道类A,那么就是单向关联。
做法:

1、B持久化类中饭加入A的set集合以及对应get set方法

2、在B表的映射文件中加入Set信息以及一对多信息:

这里写代码片

hibernate 希望你先建实体类 然后配置信息 再生成表
实际开发的时候:却是先建立表


得分清楚:
允许重复的是多
有单个对象的就是多
有set集合的是一

person{
id 
name
aid
}
Address {
aid
addr
set<person> person
}

<class name="Person">
    <id name="id" column="personId">
            <generator class="native"/>
    </id>

    <many-to-one name="address"   column="addressId"   not-null="true"/>
</class>

<class name="Address">
//可以设置 非空约束
    <id name="id" column="addressId" not-null="true"/>
        <generator class="native"/>
    </id>
    //在address这头有个set集合
    <set name="people" inverse="true">
          //key  就是自己的id 即aid
        <key column="addressId"/>
         //绑定的class 是person类
        <one-to-many class="Person"/>
    </set>
</class
>

在见表语句中 我们可以发现:
外键约束 建立在person身上

单纯看表 是看不出双向关系的 关系维护是建立在person表中的
一个地址下面 对应多个person

再次强调:
用户 p1有个订单 101 ,101订单 对象商品明细(手机 冰箱 电视 扫地机器人 ),

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值