从零开始 Spring Boot 60:一个实体映射到多个表

从零开始 Spring Boot 60:一个实体映射到多个表

spring boot

图源:简书 (jianshu.com)

在之前的文章中我们讨论了 JPA 中的一对一关系,实际上存在一种特殊的一对一关系,即将一个实体映射到多张表,本文会讨论这种关系。

我之前提过,有时候会因为性能上的考量将一张表拆分成多张表,虽然拆分后也可以用一对一关系来表示和实现,但这样并不是特别合适,因为一对一关系中一边的关系是可以为null的,比如说一个学生对应一个电子邮件地址,也可能有的学生没有电子邮件地址,此时email表可能就没有一条对应的数据。但如果是从同一张表上拆分出的两张表,必然存在一对一的关系,即使另一张表中的数据都是null字段。

下面我们将说明如何实现这种关系。

示例

假设我们有一张表,保存学生的所有信息,这个表可以用下面的实体类表示:

@Entity
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank
    @Length(max = 45)
    private String name;
    @NotNull
    private Boolean loveMusic = false;
    @NotNull
    private Boolean loveDraw = false;
    private String address;
    private String email;
}

Hibernate 生成的表结构如下:

CREATE TABLE `student` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  `address` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `love_draw` bit(1) NOT NULL,
  `love_music` bit(1) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

假如这个表有性能瓶颈,我们需要将其中的爱好相关字段拆分出来,但我们又不希望改变对这个实体的使用习惯,即我们依然希望通过这个实体的属性访问相关数据。

@SecondaryTable

为了实现这个目的,可以按下面的方式修改实体类:

@Entity(name = "Student2")
@Table(name = "student2")
@SecondaryTable(name = "student_hobbies2",
        pkJoinColumns = @PrimaryKeyJoinColumn(name = "student_id"))
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank
    @Length(max = 45)
    private String name;
    @NotNull
    @Column(table = "student_hobbies2")
    private Boolean loveMusic = false;
    @NotNull
    @Column(table = "student_hobbies2")
    private Boolean loveDraw = false;
    private String address;
    private String email;
}

这里用@SecondaryTable注解来表示要拆分出去的副表,name表示副表的名称,pkJoinColumns表示用于关联两张表的主键(外键)。

此外,还需要将拆分出去的字段相关的属性的@Column注解标注为副表字段(table=xxx)。

现在 Hibernate 生成的表结构为:

CREATE TABLE `student2` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `address` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

CREATE TABLE `student_hobbies2` (
  `love_draw` bit(1) NOT NULL,
  `love_music` bit(1) NOT NULL,
  `student_id` bigint NOT NULL,
  PRIMARY KEY (`student_id`),
  CONSTRAINT `FKp37nmekoeubhhvwpsw0euvps3` FOREIGN KEY (`student_id`) REFERENCES `student2` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

但对于实体的调用方来说,不会有任何影响,依然可以用之前的方式使用实体类,甚至不会“察觉”这个实体中的字段已经拆分成了两张表,这就是实体抽象层的好处。

@SecondaryTables

可以用多个@SecondaryTable让一个实体对应多张表:

// ...
@SecondaryTable(name = "student_hobbies3",
        pkJoinColumns = @PrimaryKeyJoinColumn(name = "student_id"))
@SecondaryTable(name = "student_detail3",
        pkJoinColumns = @PrimaryKeyJoinColumn(name = "student_id"))
public class Student {
	// ...
    @NotNull
    @Column(table = "student_hobbies3")
    private Boolean loveMusic = false;
    @NotNull
    @Builder.Default
    @Column(table = "student_hobbies3")
    private Boolean loveDraw = false;
    @Column(table = "student_detail3")
    private String address;
    @Column(table = "student_detail3")
    private String email;
}

JDK8 之前的版本不支持重复注解,可以这样:

@SecondaryTables({
        @SecondaryTable(name = "student_hobbies3",
                pkJoinColumns = @PrimaryKeyJoinColumn(name = "student_id")),
        @SecondaryTable(name = "student_detail3",
                pkJoinColumns = @PrimaryKeyJoinColumn(name = "student_id"))})
public class Student {
	// ...
}

@Embeddable 和 @Embedded

之前我们介绍过@Embeddable@Embedded的用途,映射到副表的属性同样可以用类似的方式进行分组:

@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "Student4")
@Table(name = "student4")
@SecondaryTable(name = "student_hobbies4",
        pkJoinColumns = @PrimaryKeyJoinColumn(name = "student_id"))
@SecondaryTable(name = "student_detail4",
        pkJoinColumns = @PrimaryKeyJoinColumn(name = "student_id"))
public class Student {
    @ToString
    @Embeddable
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Hobbies {
        @NotNull
        @Builder.Default
        @Column(table = "student_hobbies4")
        private Boolean loveMusic = false;
        @NotNull
        @Builder.Default
        @Column(table = "student_hobbies4")
        private Boolean loveDraw = false;
    }

    @ToString
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Embeddable
    public static class Details {
        @Column(table = "student_detail4")
        private String address;
        @Column(table = "student_detail4")
        private String email;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank
    @Length(max = 45)
    @Column(unique = true)
    private String name;
    @NotNull
    @Embedded
    @Builder.Default
    private Hobbies hobbies = new Hobbies();
    @NotNull
    @Embedded
    @Builder.Default
    private Details details = new Details();
}

如果@Embeddable标记的类需要重用,也可以使用@AttributeOverrides注解,具体可以看这篇文章。

The End,谢谢阅读。

本文的完整示例可以从这里获取。

参考资料

  • 从零开始 Spring Boot 56:JPA中的一对一关系 - 红茶的个人站点 (icexmoon.cn)
  • Mapping a Single Entity to Multiple Tables in JPA

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/34568.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何将采购报价请求流程自动化?

在当今快节奏的商业环境中,效率和速度是保持竞争力的关键。在采购管理中,企业经常为简化操作而努力的一个领域是报价请求过程。手动处理请求、与供应商沟通并跟踪答复,可能非常耗时且容易出错。 然而,随着像8Manage SRM等专业电子…

Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库

介绍 Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的电子表格文档。支持 XLAM / XLSM / XLSX / XLTM / XLTX 等多种文档格式&a…

Git下载与安装(windows)

文章目录 Git 简介Git 下载Git 安装Git 环境变量配置 Git 简介 Git 是一种分布式版本控制系统,用于快速、高效地处理任何大小规模的项目。它是由 Linus Torvalds 在 2005 年开始开发的,Git 最初是为了管理 Linux 内核源代码而创建的。现在 Git 是一个广…

Spring 是什么框架?

对于一门技术,我们需要从为什么要学、学什么以及怎么学这三个方向入手来学习。那在说Spring这三点之前,我们先看Spring之前要学什么。 Java基础、Java语法进阶、MySQL基础操作、MySQL数据库设计和多表操作、JDBC、Maven基础、MyBatis、HMLCSS、JavaScrip…

PCA主成分分析

PCA降维算法 目前图像特征的提取主要有两种方法:传统图像特征提取方法 和 深度学习方法。 传统的特征提取方法:基于图像本身的特征进行提取(PCA);深度学习方法:基于样本自动训练出区分图像的特征分类器&a…

SpringBoot--超时熔断器

需求背景 如果一个服务中有很多涉及需要服务间熔断的地方,就会出现N多下述代码: 1.N个fegnClient接口 FeignClient(name "hello-world-service", fallback HelloWorldFallback.class) public interface HelloWorldService {GetMapping(&q…

Python 学习之NumPy(一)

文章目录 1.为什么要学习NumPy2.NumPy的数组变换以及索引访问3.NumPy筛选使用介绍筛选出上面nb数组中能被3整除的所有数筛选出数组中小于9的所有数提取出数组中所有的奇数数组中所有的奇数替换为-1二维数组交换2列生成数值5—10,shape 为(3,5)的二维随机浮点数 NumP…

Apache Doris (十七) :Doris分区和分桶3-分桶及建议

​​​目录 一、分桶Bucket ​​​​​​​二、分区和分桶数量和数据量的建议 进入正文之前,欢迎订阅专题、对博文点赞、评论、收藏,关注IT贫道,获取高质量博客内容! 一、分桶Bucket Doris数据表存储中,如果有分区&…

使用docker搭建mysql集群

一、技术架构 1、架构图 2、解说 mysql_1、mysql_2、mysql_3是一组主从模式,同理mysql_4、mysql_5、mysql_6也是一组主从模式从上面的图可以看出mysql_1和mysql_4是主节点,可以进行增删改查操作,但是子几点只能查询操作如果mysql_1节点出现问题了,有mysql_4节点组…

SDK和API是什么?SDK和API的区别与联系

目录 💌 SDK和API是什么? SDK API 💌 SDK和API的区别 💌 该如何选择 API 和 SDK 对接? 💌 SDK和API是什么? SDK SDK的概念:软件开发工具包(全称:Softwa…

黑盟菜鸟剪辑短视频助手是什么

今天我们来讲一下视频综合处理功能,首先我们来打开软件主界面,通过模块化功能合集视频综合处理进入我们的这个功能。首先我们来看一下功能的布局,左边是导入视频的地方,右边是功能区,这里总共包括 32 种功能&#xff0…

计算机基础--->数据结构(6)【AVL树(平衡二叉树)】

文章目录 AVL(平衡二叉树)树性质AVL树的操作(Java)节点的创建AVL树的插入1.判断平衡2.保持树的平衡3.判断是否AVL树4.删除节点 全部代码 AVL(平衡二叉树)树 平衡二叉树是一种特殊的二叉搜索树,他…

【nav_msgs/Path.h发布路径】

#include <nav_msgs/Path.h> 是一个 ROS (Robot Operating System) 中的包含文件。它是用于包含 nav_msgs/Path 消息类型的头文件,这是一个标准的 ROS 消息类型。 nav_msgs/Path 消息类型常用于机器人导航系统中,以表示路径。这种路径通常由一系列的位置点组成,这些点…

解决:yarn 无法加载文件 “C:\Users\admin\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本“ 的问题

1、问题描述&#xff1a; 其一、报错的整体代码为&#xff1a; yarn : 无法加载文件 C:\Users\admin\AppData\Roaming\npm\yarn.ps1&#xff0c;因为在此系统上禁止运行脚本 // 整体的报错代码为 &#xff1a; yarn : 无法加载文件 C:\Users\admin\AppData\Roaming\npm\yar…

Linux环境搭建(三)— 搭建数据库服务器

linux &#xff08;ubuntu&#xff09;安装mysql 和环境配置 一、安装MySql二、配置环境三、外网访问四、重置密码五、卸载 写在前面&#xff1a; 本文默认你的Linux系统已经安装vim&#xff0c;yum等&#xff0c;如你使用的是一个全新的操作系统&#xff0c;移步上一篇开始配置…

迪赛智慧数——柱状图(象形动态图):高考填报专业考虑的因素

效果图 填报志愿是高考后的一大重要环节&#xff0c;你的职业生涯就在这里起航了。那么&#xff0c;应该怎么填报志愿呢&#xff1f;高考填报专业考虑的因素很多&#xff0c;过半的人会考虑专业就业前景及薪资&#xff0c;其次是个人兴趣和是否为双一流建设学科。 数据源&…

基于深度学习的高精度动物园动物检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度动物园动物&#xff08;水牛、斑马、大象、水豚、海龟、猫、奶牛、鹿、狗、火烈鸟、长颈鹿、捷豹、袋鼠、狮子、鹦鹉、企鹅、犀牛、羊、老虎&#xff09;检测识别系统可用于日常生活中或野外来检测与定位动物园动物&#xff0c;利用深度学…

【算法与数据结构】541、LeetCode反转字符串 II

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题自己写了一个swap函数&#xff0c;用来反转字符串&#xff0c;也可以用库函数reverse。然后是用in…

mac上使用brew安装mysql5.7

使用Homebrew进行MySQL数据库的安装需要MacOS系统中已经安装了相关环境 1.查询软件信息 首先使用search命令搜索MySQL数据库完整名称&#xff1a; brew search mysql可以看到5.7版本的MySQL数据库完整名称是mysql5.7 2. 执行安装命令 使用install命令进行软件安装&#xf…

基于单片机电子密码锁射频卡识别指纹门禁密码锁系统的设计与实现

功能介绍 通过指纹进行开锁或者是按键输入当前的密码&#xff0c;修改密码&#xff0c;对IC卡可以进行注册&#xff0c;删除。当有RFID卡进入到读卡器的读卡范围内时&#xff0c;则会自动读取卡序列号&#xff0c;单片机根据卡的序列号对卡进行判断。若该卡是有效卡&#xff0c…