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

news2025/4/5 4:27:16

从零开始 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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

elementui实现表格自定义排序

需求说明&#xff1a; 1、第一行不参与排序 2、实现带%排序 3、实现null值排序 4、实现值相等不排序 5、实现含有占位符‘–‘排序放到最后 效果图如下&#xff1a; <template> <div><template><el-table border :data"previewTableData" style…

ROS学习之从yaml文件中读取多传感器坐标系之间的静态TF关系并发布Topic

文章目录 0 引言1 工作空间创建并初始化2 创建ROS程序包3 创建yaml读取函数3.1 yaml文件3.2 读取函数 4 创建静态TF关系的发布主函数5 创建launch文件6 编辑CMakeLists.txt7 编译运行7.1 编译7.2 运行 0 引言 机器人中经常使用多种传感器来做定位和建图&#xff0c;而多个传感…

找出一个List中每个元素出现的次数

文章目录 一、需求&#xff1a;找出一个list中&#xff0c;每个元素出现的次数1. 普通实现&#xff08;hashmap&#xff09;&#xff1a;1.1 代码实现&#xff1a;1.2运行结果&#xff1a;1.3 案例分析&#xff1a; 2. 普通实现&#xff08;HashSet#Collections.frequency&…

Flask学习笔记(2)应用部署

本文将介绍如何部署Flask应用。   部署Flask应用&#xff0c;主要是要运用多线程与多进程&#xff0c;提高接口的并发能力。我们以下面的Python代码&#xff08;server.py&#xff09;为例进行演示&#xff1a; # -*- coding: utf-8 -*- import time import datetime from f…

04-HAL库UART配置及协议解析设计

本节内容介绍 1、HAL库UART 在cubemx中的配置及注意事项;2、HAL库UART详解与结构介绍;3、实现简单地UART数据收发&#xff1b; 源码地址&#xff1a; HAL库UART在cubemx中的配置 串口原理图 串口1咱们已经用作rtt的print使用了&#xff0c;所以使用另外一组串口来进行串口…

android studio 4.0以上隐藏调用方法参数名提示

引入&#xff1a; android studio在编辑代码的时候&#xff0c;调用函数时会接口处会自动提示参数名&#xff0c;方便代码书写时对传参命名的规范性。 可以如果代码是魂效过的&#xff0c;那会适得其反&#xff0c;l,l1,l2,i,i1,i2这样的参数名提醒反而会混淆视听。 这时候可…

图书馆机器人的应用,科技助力新趋势

随着科技的发展和智能化时代的到来&#xff0c;图书馆越来越多地引入了机器人作为服务和管理的工具。图书馆机器人可以轻松地完成多种任务&#xff0c;包括为用户提供导航服务、管理借还书、整理图书、清扫图书馆等。 首先&#xff0c;图书馆机器人可以为用户提供导航服务。在庞…

讲讲仿真软件的文件导入

仿真软件识别导入的设计文档是有区别的&#xff0c;实际的使用经历&#xff0c;ADS只是用于搭建Channel通道仿真&#xff0c;那本文以Cadence的Sigrity和Ansys的SIwave为例&#xff0c;讲讲仿真软件的文件导入。 先以Sigrity为例&#xff0c;打开软件&#xff0c;File 菜单Open…

记一次 JVM 参数调整导致 ShardingSphere-Proxy 性能下降的问题排查过程

问题现象 在性能测试中&#xff0c;分别对两个版本差异间隔一天的 ShardingSphere-Proxy 做性能测试&#xff0c;发现版本更新的 Proxy 比旧的 Proxy 在 TPC-C 场景下峰值 tpmC 下降了 7% 左右。 排查过程 在性能测试期间&#xff0c;使用 async-profiler 分别对两个进程进行…

根据aop实现自定义缓存注解

根据aop实现自定义缓存注解 自定义注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.concurrent.TimeUnit;/*** author: yanche…

ESP32通过Arduino导出编译bin文件并进行量产烧录

ESP32通过Arduino导出编译bin文件并进行量产烧录 文章目录 ESP32通过Arduino导出编译bin文件并进行量产烧录Arduino导出编译的bin文件方法一&#xff1a;通过Arduino IDE提供的工具导出编译文件方法二&#xff1a;到编译生成的临时文件目录进行查找 弄清楚Arduino的编译烧录过程…

Unity3D:工具栏

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 工具栏 在 Unity Editor 顶部可以看到工具栏。 工具栏不是窗口&#xff0c;是 Unity 界面中唯一无法重新排列的部分。 有关场景视图中的其他工具&#xff0c;请参阅叠加。 工具栏…

记录一次使用thinkphp5分页器获取数据

// 输出当前页 $nowPage $data->currentPage(); // 输出总条数 $total $data->total(); // 输出当前页条数 $listRows $data->listRows();db(tablename)->where("id > 0")->paginate(10,true,[page>4]); //每页显示10条记录&#xff0c;且打…

C#(五十一)之特性

特性是用于为程序元素添加额外信息的一种机制。比如记录文件修改时间、提示某方法已经过期等。方法、变量、属性、类、接口、结构体以及程序及都是程序元素 Obsolete第二个参数设置为true,调用此方法会产生警告并引起编译器报错 百度了一下C#还有其他的特性以及自定义特性&am…

基于ssm实现图书商城(spring+springmvc+mybatis)

一、项目功能 前台 图书基本展示,包括推荐图书展示和类图书类型展示.推荐图书包括条幅推荐,热销推荐和新品推荐.按照图书类型展示商品.图书详细信息展示.图书加入购物车.修改购物车内图书信息,例如数量等.用户登录.用户注册.修改个人信息,包括密码和收获信息.购物车付款.用户…

前置声明、源文件include、编译链接顺序问题

TestB.h (前置声明&#xff0c;无需在源文件include)重点&#xff1a; 1.前置声明用在指针变量使用&#xff0c;无需在头文件或源文件include 2.继承或者普通变量在头文件使用的时候(除非所有的编译顺序都正确&#xff0c;才能在源文件include)&#xff0c;最好不要在源文件i…

汇编语言基础--内中断

在8086CPU实模式下有如下内存布局&#xff1a; 我们看到在000-3FF的位置是放着中断向量表。 里面放的其实是4个字节的地址&#xff08;地址处放着对应的中断处理函数&#xff09;。我们知道在8086实模式下&#xff0c;是通过cs:ip来找到要执行的指令。cs是2个字节&#xff0c;i…

chat2DB使用教程

1. chat2DB简介 1-1. 简介 ​ chat2DB是一款有开源免费的多数据库客户端工具&#xff0c;支持windows、mac本地安装&#xff0c;也支持服务器端部署&#xff0c;web网页访问。和传统的数据库客户端软件Navicat、DBeaver 相比Chat2DB集成了AIGC的能力&#xff0c;能够将自然语…

Squid 缓存代理--反向代理

Squid 缓存代理–反向代理 反向代理&#xff1a;如果Squid反向代理服务器中缓存了该请求的资源&#xff0c;则将该请求的资源直接返回给客户端&#xff1a;否则反向代理服务器将向后台的WEB服务器请求资源&#xff0c;然后将请求的应答返回给客户端&#xff0c;同时也将应答缓…

VMware 虚拟磁盘格式

1.如果必须用FT &#xff0c; 只能选eagerzeroedthick 2.如果追求最佳应用性能不考虑空间占用 &#xff0c; 选eagerzeroedthick 3.如果希望最大成都的利用空间&#xff0c;并且对磁盘的增长是可控的&#xff0c;可以选thin格式 4.如果不希望空间的过量分配(oversubsribe)造…