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

行动起来,活在当下

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

目 录CONTENT

文章目录

Hibernate(5)之放弃维护外键关系

zze
zze
2017-07-16 / 0 评论 / 0 点赞 / 368 阅读 / 3510 字

不定期更新相关视频,抖音点击左上角加号后扫一扫右方侧边栏二维码关注我~正在更新《Shell其实很简单》系列

假入表中已有如下数据:

image.png

现要把张三从 1 班转到 2 班,操作如下:

Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = session.get(Student.class, 1); // 获取张三
Class clazz = session.get(Class.class, 1); // 获取 2 班
clazz.getStudents().add(student);
student.setClazz(clazz);
transaction.commit();
/*
此时会执行一下两条 SQL:
Hibernate:
    update
        student
    set
        name=?,
        age=?,
        birthday=?,
        address=?,
        cid=?
    where
        id=?
Hibernate:
    update
        student
    set
        cid=?
    where
        id=?
 */

可以看到上面示例执行了两条 SQL ,原因是:在上述代码中获取了两个持久态对象,并且在事务提交时两个持久态对象的属性都发生了变化,所以 Hibernate 发出了两条 SQL。

但显然这个操作其实一条 SQL 就能搞定的,这个时候就需要控制一方不维护外键关系了。

**‘一’的一方放弃维护外键关系: **

<!-- com/zze/bean/Class.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.Class" table="class">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="32"/>
        <set name="students" cascade="save-update" inverse="true">
            <key column="cid"/>
            <one-to-many class="com.zze.bean.Student" />
        </set>
    </class>
</hibernate-mapping>

依旧是执行上述代码,会发现只会由‘多’(学生)的一方发出一条 update 语句。

在 Hibernate 中,‘多’的一方不可以放弃维护外键关系。

为什么‘多’的一方不能放弃维护外键关系?换句话说为什么 Hibernate 要让多的一方来维护关系?

原因是当‘一’的一方维护关系时,Hibernate 会额外发出一条 select 语句查出它所关联的所有的多的一方,显然没必要。而多的一方维护关系就比较简单了,直接 update 它本身的外键即可。

看到过一个有趣应景的说法:十三亿人民(‘多’)记住(维护)国家领导人的名字很简单,但要想国家领导人(‘一’)记住(维护)十三亿就不可能了。

理解 inverse 和 cascade:

// com/zze/bean/Class.hbm.xml 的 set 中设置了 cascade="save-update"、inverse="true"
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setName("王五");
Class clazz = session.get(Class.class, 2); // 获取 1 班
clazz.getStudents().add(student);
session.save(clazz);
transaction.commit();

/*
上述代码执行结果会是怎么样?
    先明确 cascade 和 inverse 的作用:
        cascade:为 save-update,即 Class 会级联保存和更新 Student。
        inverse:为 true,即 Class 放弃维护外键关系。
    首先 session 保存的是 clazz,clazz 是持久态对象。
    在事务提交时 clazz 的外键字段 students 发生了变化,添加了 student,
    班级有级联操作学生的能力,所以“王五”这个 student 会被保存到数据库。
    而班级放弃了维护外键关系,所以“王五”这个 student 添加到数据库后对应的外键字段为 null。
结果如下:
    mysql> select * from student;
    +----+--------+------+----------+---------+------+
    | id | name   | age  | birthday | address | cid  |
    +----+--------+------+----------+---------+------+
    |  1 | 张三   | NULL | NULL     | NULL    |    1 |
    |  2 | 王五   | NULL | NULL     | NULL    | NULL |
    +----+--------+------+----------+---------+------+
    2 rows in set (0.00 sec)
 */
0

评论区