0

0

Hibernate中自引用多对多关系的正确映射实践

花韻仙語

花韻仙語

发布时间:2025-11-01 15:31:01

|

410人浏览过

|

来源于php中文网

原创

hibernate中自引用多对多关系的正确映射实践

本文详细阐述了如何在Hibernate中正确映射自引用的多对多关系,特别适用于处理如父子层级结构等场景。通过使用@ManyToMany和@JoinTable注解,并精确配置joinColumns和inverseJoinColumns,我们能够在一个实体内优雅地管理其自身的父节点和子节点列表,从而实现复杂的数据关联模型。

1. 理解自引用多对多关系

自引用多对多关系是指一个实体类通过一个中间关联表,与自身建立多对多的关联。这种关系在实际应用中非常常见,例如:

  • 父子层级结构:一个节点可以有多个父节点和多个子节点。
  • 朋友关系:一个人可以有多个朋友,朋友之间是相互的。
  • 商品推荐:一个商品可以推荐多个相关商品,反之亦然。

本教程将以一个典型的父子层级结构为例,其中一个Test实体可以拥有多个父Test实体和多个子Test实体。

2. 数据库模型设计

为了实现这种自引用多对多关系,我们需要两个表:

  1. test_table:存储Test实体的数据。
  2. relation:作为中间关联表,连接test_table中的不同行,表示它们之间的父子关系。

具体的表结构如下:

test_table

  • id:主键,自增长。
  • comment:其他业务字段。

relation

TP-COUPON 导购系统 免费版
TP-COUPON 导购系统 免费版

自从百度屏蔽淘宝客网站、淘宝抛弃淘宝客之后,个人站长集体陷入了恐慌之中。此时,什么值得买网的异军突起引起了广大个人站长的极大关注。做一个什么值得买一样的导购网站成了众多个人站长的一致心愿! TP-COUPON 导购系统 即是让个人站长实现此心愿的绝佳选择! 欢迎个人站长选用。V1.1版 更新记录:1.修正请求时查询淘宝店铺错误的bug2.删除一些无用的代码

下载
  • id:主键,自增长。
  • a_id:外键,指向test_table.id,代表子节点的ID。
  • a_parent_id:外键,指向test_table.id,代表父节点的ID。
  • (a_id, a_parent_id):联合唯一约束,确保一对父子关系只存在一次。

示例数据:

a_id | a_parent_id
-----|------------
1    | null       (节点1没有父节点)
2    | null       (节点2没有父节点)
3    | 1          (节点3的父节点是1)
4    | 1          (节点4的父节点是1)
5    | 2          (节点5的父节点是2)
6    | 5          (节点6的父节点是5)
6    | 4          (节点6的父节点是4,注意这里节点6有两个父节点)

从示例数据可以看出,一个节点可以有多个父节点(如节点6),也可以有多个子节点(如节点1有子节点3和4)。

3. Hibernate实体映射

在Hibernate中映射这种关系的核心在于使用@ManyToMany注解,并结合@JoinTable来定义中间关联表及其连接列。由于我们需要在一个Test实体中同时访问其父节点和子节点,因此需要分别定义两个List集合。

首先,Test实体基础结构如下:

import javax.persistence.*;
import java.util.List;
import java.util.ArrayList; // 推荐初始化列表以避免NullPointerException

@Entity
@Table(name = "test_table")
public class Test {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false)
    private Long id;

    @Column
    private String comment;

    // 构造函数、Getter和Setter方法省略
    // ...
}

接下来,我们为父节点和子节点列表添加映射:

import javax.persistence.*;
import java.util.List;
import java.util.ArrayList;

@Entity
@Table(name = "test_table")
public class Test {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false)
    private Long id;

    @Column
    private String comment;

    /**
     * 映射当前Test实体的所有父节点。
     * 关系:当前Test实例是子节点,其对应的a_id在relation表中。
     *      父Test实例是父节点,其对应的a_parent_id在relation表中。
     *
     * @JoinTable: 定义中间关联表 "relation"。
     *   name: 指定关联表的名称。
     *   joinColumns: 定义当前实体(Test)在关联表中的外键列。
     *     @JoinColumn(name = "a_id", referencedColumnName = "id"):
     *       name="a_id" 表示在"relation"表中,存储当前Test实体ID的列是"a_id"。
     *       referencedColumnName="id" 表示"a_id"列引用的是"test_table"中的"id"列。
     *   inverseJoinColumns: 定义关联实体(父Test)在关联表中的外键列。
     *     @JoinColumn(name = "a_parent_id", referencedColumnName = "id"):
     *       name="a_parent_id" 表示在"relation"表中,存储父Test实体ID的列是"a_parent_id"。
     *       referencedColumnName="id" 表示"a_parent_id"列引用的是"test_table"中的"id"列。
     */
    @ManyToMany(targetEntity = Test.class, fetch = FetchType.LAZY)
    @JoinTable(
        name = "relation",
        joinColumns = @JoinColumn(name = "a_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "a_parent_id", referencedColumnName = "id")
    )
    private List parents = new ArrayList<>();

    /**
     * 映射当前Test实体的所有子节点。
     * 关系:当前Test实例是父节点,其对应的a_parent_id在relation表中。
     *      子Test实例是子节点,其对应的a_id在relation表中。
     *
     * @JoinTable: 定义中间关联表 "relation"。
     *   name: 指定关联表的名称。
     *   joinColumns: 定义当前实体(Test)在关联表中的外键列。
     *     @JoinColumn(name = "a_parent_id", referencedColumnName = "id"):
     *       name="a_parent_id" 表示在"relation"表中,存储当前Test实体ID的列是"a_parent_id"。
     *       referencedColumnName="id" 表示"a_parent_id"列引用的是"test_table"中的"id"列。
     *   inverseJoinColumns: 定义关联实体(子Test)在关联表中的外键列。
     *     @JoinColumn(name = "a_id", referencedColumnName = "id"):
     *       name="a_id" 表示在"relation"表中,存储子Test实体ID的列是"a_id"。
     *       referencedColumnName="id" 表示"a_id"列引用的是"test_table"中的"id"列。
     */
    @ManyToMany(targetEntity = Test.class, fetch = FetchType.LAZY)
    @JoinTable(
        name = "relation",
        joinColumns = @JoinColumn(name = "a_parent_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "a_id", referencedColumnName = "id")
    )
    private List children = new ArrayList<>();

    // 构造函数、Getter和Setter方法
    public Test() {}

    public Test(String comment) {
        this.comment = comment;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public List getParents() {
        return parents;
    }

    public void setParents(List parents) {
        this.parents = parents;
    }

    public List getChildren() {
        return children;
    }

    public void setChildren(List children) {
        this.children = children;
    }

    // 辅助方法,方便添加和移除关系
    public void addParent(Test parent) {
        this.parents.add(parent);
    }

    public void addChild(Test child) {
        this.children.add(child);
    }
}

4. 关键注解详解

  • @ManyToMany(targetEntity = Test.class):
    • 表示这是一个多对多关系。
    • targetEntity = Test.class 指明关联的目标实体是Test类自身。
  • @JoinTable(name = "relation", ...):
    • 指定用于连接两个实体(这里是Test实体与Test实体自身)的中间关联表的名称为relation。
  • joinColumns:
    • 定义了拥有此@ManyToMany注解的实体(即当前Test实例)在中间关联表relation中的外键列。
    • 例如,对于parents列表,当前Test实例是子节点,它的ID存储在relation.a_id中,因此joinColumns = @JoinColumn(name = "a_id", referencedColumnName = "id")。
      • name = "a_id":指定relation表中的列名。
      • referencedColumnName = "id":指定test_table中被引用的主键列名。
  • inverseJoinColumns:
    • 定义了关联实体(即List中的Test实例)在中间关联表relation中的外键列。
    • 例如,对于parents列表,List中的Test实例是

相关专题

更多
hibernate和mybatis有哪些区别
hibernate和mybatis有哪些区别

hibernate和mybatis的区别:1、实现方式;2、性能;3、对象管理的对比;4、缓存机制。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

141

2024.02.23

Hibernate框架介绍
Hibernate框架介绍

本专题整合了hibernate框架相关内容,阅读专题下面的文章了解更多详细内容。

84

2025.08.06

Java Hibernate框架
Java Hibernate框架

本专题聚焦 Java 主流 ORM 框架 Hibernate 的学习与应用,系统讲解对象关系映射、实体类与表映射、HQL 查询、事务管理、缓存机制与性能优化。通过电商平台、企业管理系统和博客项目等实战案例,帮助学员掌握 Hibernate 在持久层开发中的核心技能。

35

2025.09.02

Hibernate框架搭建
Hibernate框架搭建

本专题整合了Hibernate框架用法,阅读专题下面的文章了解更多详细内容。

64

2025.10.14

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

468

2024.01.03

python中class的含义
python中class的含义

本专题整合了python中class的相关内容,阅读专题下面的文章了解更多详细内容。

13

2025.12.06

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

356

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2077

2023.08.14

c++ 根号
c++ 根号

本专题整合了c++根号相关教程,阅读专题下面的文章了解更多详细内容。

58

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.8万人学习

C# 教程
C# 教程

共94课时 | 7.5万人学习

Java 教程
Java 教程

共578课时 | 50.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号