06. Hibernate 中主键映射的助攻

1. 前言

本节课和大家一起聊聊 Hibernate 中的主键策略。通过本节课程,你将了解到:

  • 什么是主键策略及主键生成器的种类;
  • 如何映射复合主键。

2. 主键策略

Hibernate 进行数据库操作时,可依靠主键生成器组件更快速、准确地进行一系列操作。这便是主键策略

2.1 主键生成器

主键是关系数据库中的概念,目的是唯一标识表中记录,保证实体数据的完整性。

  • 关系数据库中表与表中数据的关系描述需依赖主键实现 ;
  • 另有外键概念,所谓外键是在另一张表中对引用表的主键值的引用称呼。

主外键关系指在不同的表中通过共同的字段信息建立起表中数据依赖(引用)关系。

回到 Hibernate 的世界!先展示一段代码:

Student student = new Student(2, "Configuration老二", "男");
session.save(student);

上面的代码功能:把应用程序中的数据写入到数据库中,没毛病呀!

来!没毛病找点毛病出来:

实际操作时,要求 Hibernate 把程序中 stuId 属性的值插入到表中同名的 stuId 主键字段中。

主键有什么特点?

唯一性!回答得对。

请问在应用程序中构建数据时,如何确保赋值给 stuId 的值在表中不存在!这就是问题所在。

如何解决?

使用 Hibernate 主键生成器。

所谓主键生成器其作用就是在 Hibernate 向表中插入数据时,负责生成表中数据记录的主键。

Hibernate 主键生成器 API 介绍:

  • Hibernate 的主键生成器(generator)都实现了 org.hibernate.id.IdentityGenerator 接口;
 public class IdentityGenerator extends AbstractPostInsertGenerator { …… }
  • 开发者可以遵循这个接口规范提供自己的主键生成方案;
  • Hibernate 内置有较多主键生成器主键生成器都有自己的实现类,并提供有快捷名称方便在注解或 XML 中引用。

常用主键生成器一览:

  • org.hibernate.id.IncrementGenerator(increment):对 longshort 或 int 的数据列生成自动增长主键;

  • org.hibernate.id.IdentityGeneratoridentity): 适用于 SQL server,MySql 等支持自动增长列的数据库,适合 longshort 或 int 数据列类型;

  • org.hibernate.id.SequenceGeneratorsequecne):适用 oracle,DB2 等支持 Sequence 的数据库,适合 long、short 或 int 数据列类型;

  • org.hibernate.id.UUIDGeneratoruuid):对字符串列的数据采用 128 - 位 uuid 算法生成唯一的字符串主键;

  • org.hibernate.id.Assigned(assigned):由应用程序指定,也是默认生成策略。

默认使用 assigned 生成器。这种方案要求开发者在应用程序中提供自己的主键生成算法:

  • 调用保存方法之前,先带着指定的值往数据库中跑一趟,检索是否存在重复,如果有,再试其它值;
  • 调用保存方法之前,先检索到表中 stuId 字段值的最大值,返回应用程序后递增 1,用于 stuId 新值。如果多个用户同时向数据中插入数据,这种方案会出问题,不适合并发操作环境。

使用 assigned 生成器除非有一个很完美的解决方案,否则建议只用于学习或测试环境。

本课程使用的是 Mysql 数据库,最佳选择 identity 生成器,主键值交给数据库的自动增长列自动生成。

2.2 使用主键生成器重构代码

1.在 Student 类的标识属性(stuId)上标注如下注解;

 @Id@GeneratedValue(strategy=GenerationType.IDENTITY)public Integer getStuId() {return stuId;}

简单得难以置信!空灵而干净!!

使用 @GeneratedValue 注解确定主键生成器类型。GenerationType 是一个枚举类型,有如下几个选择:

  • AUTOHibernate 区分数据库系统,自动选择最佳策略;
  • IDENTITY: 适合具有自动增长类型的数据库,如 MySql……
  • SEQUENCE: 适合如 Oracle 类型数据库;
  • TABLE: 使用 Hibernate 提供的 TableGenerator 生成器,不常用。

2.为了更好观察生成的新数据,重建数据库中的表。主配置文件中修改或添加如下配置信息;

 <property name="hbm2ddl.auto">create</property>

3.执行插入数据实例;

  // 打开事务try{transaction = session.beginTransaction();// 添加一条学生信息,此处没有指定学生编号Student student = new Student("Hibernate 01", "男");session.save(student);transaction.commit();  } catch(Exception e) {transaction.rollback(); } finally {session.close();}

3.进入 Mysql 系统查看,表结构中 stuId 自动设为主键,且为自动递增;

4.查看表中数据,主键值自动生成;

5.试着多加几条数据,别忘记修改如下配置信息。

 <property name="hbm2ddl.auto">update</property>

 

大功告成!!

2.3 主键生成器

使用注解 @GeneratedValue 指定生成器类型后,Hibernate 一般情况下会自动创建对应的生成器对象,如前面指定类型为 IDENTITY,则创建生成 org.hibernate.id.IdentityGenerator 对象。

如果需要个性化定制生成器对象,则需要显示指定生成器对象,如为 Oracle 数据库指定主键生成器时,则配置可如下:

XML 映射方式:

<id name="stuId" type="Integer" column="stuId"><generator class="sequence"><param name="sequence">mySeq</param></generator>
</id>

注解映射方式:

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="mySeqIdGen")
@SequenceGenerator(name="mySeqIdGen",sequenceName="mySeq")
public Integer getStuId() {return stuId;
}

@SequenceGenerator 注解显示指明使用 org.hibernate.id.SequenceGenerator 生成器对象,并指定使用数据库中的命名为 mySeq 的序列化器。

其它主键生成器的使用本文不再复述,抛砖引玉,学习者可自行深入!

3. 复合主键

3.1 什么是复合主键

关系数据库中,主键可指定一个字段实现,也可指定多个字段实现,这样的主键叫复合主键

从数据库表设计原则分析,尽可能少用复合主键,但并不排除需要使用的场景。

对使用 Hibernate 的开发者而言,将面对一个新问题:在应用程序中,如何映射表中的复合主键?

应用程序中,复合主键映射方案有三:

  • 嵌入类注解为 @Embeddable,并将实体类的属性注解为 @Id;
  • 实体类的属性注解为 @EmbeddedId;
  • 实体类注解为 @IdClass,并将该实体类所有属于主键的属性都注解为 @Id

先分清楚两个概念:

  • 实体类:使用 @entity 注解的类;
  • 嵌入类:使用 @Embeddable 注解的类;

3.2 复合主键映射方案一

实施流程

1.假设学生表中使用了 stuId,stuName 两字段构成复合主键;

2.应用程序中构建两个类;

嵌入类

 @Embeddable  public class StudentId {private Integer stuId;private String stuName;public StudentId()  {super();}      public  StudentId(Integer stuId, String stuName) {super();this.stuId = stuId;  this.stuName = stuName;}      //……省略get、set方法

嵌入类说明:

  • 标注有 @Embeddable;

  • 类中包括 stuId、stuName 两个属性与表中的复合字段相呼应;

  • 必须实现 Serializable!!!后续章节会聊到为什么。

实体类:

  @Entity  public class Student_ {private StudentId studentId;private String stuSex;public Student_() {super();}      public  Student_(StudentId studentId, String stuSex) {super();this.studentId = studentId;this.stuSex = stuSex;}      @Id      public StudentId  getStudentId() {return studentId;}      //……省略其它set、get方法

实体类说明:

实体类使用 @Entity 注解;
关键代码分析:

关键点一: 内部添加引用嵌入类属性。

     private StudentId studentId;

关键点二: studentId 属性上需要添加 @Id 注解。

    @Idpublic StudentId getStudentId() {return studentId;}

3.重新创建数据库中的学生表:

   <property name="hbm2ddl.auto">create</property>

4.运行测试实例。

Tips: 对于复合主键,需要在代码级别指定值。

   // 打开事务 transaction = session.beginTransaction();// 添加一条学生信息 Student_ student = new Student_();// 复合主键信息StudentId studentId=new StudentId(1, "Hibernate是老大");student.setStudentId(studentId);student.setStuSex("男");session.save(student);transaction.commit();

5.查看 MySql,会发现新表 student_ 中指定复合主键,且数据添加成功。

 

如上所述,嵌入类就是复合主键映射类!

3.3 复合主键映射方案二

与第一方案相比,保留 @Entity 注解的实体类。

第一方案中的嵌入类上不再使用 @Embedded 注解,嵌入类降维成普通类。

实体类中不再使用 @Id 注解,而是使用 @EmbeddedId,此注解语义明确:一注解承担两注解任务。

可理解 @EmbeddedId 注解是 @Embedded 和 @Id 两个注解的综合体。

@EmbeddedId
public StudentId getStudentId() {return studentId;
}

和第一方案一样进行代码测试,结果没什么不一样。

3.4 复合主键映射方案三

方案三与前两个方案区别:

1.没有嵌入类概念,前面的嵌入类降维成一个普通类,不加任何注解描述;

此类的作用仅仅在逻辑上把两个标识属性归为一组!

 public  class StudentId implements Serializable{private Integer stuId;    private String stuName;public StudentId() {super();}    public StudentId(Integer stuId, String stuName) {super();this.stuId = stuId;this.stuName = stuName; }    //……省略set、get方法

2.实体类中使用 @IdClass 指定内部有标识属性的类,另在实体类中也重复出现标识属性且上面使用 @Id 注解。

@Entity
//指明实体类中标注有 @Id 的属性为同一类型
@IdClass(StudentId.class)
public class Student_ {private Integer stuId;private String stuName;private String stuSex;public Student_() {super();}public Student_(Integer stuId, String stuName, String stuSex) {super();this.stuId = stuId;this.stuName = stuName;this.stuSex = stuSex;}@Idpublic Integer getStuId() {return stuId;}@Idpublic String getStuName() {return stuName;}//……省略set、get方法
}

测试代码,结果和前面 2 个方案一样。

3.5 方案比较

通过代码的编写过程,3 种方案优劣比较明显:

  • 第一种方案和第二种方案本质上没有太多区别,只是一个使用 @Id 和 @Embeddable 两个注解;一个是使用 @EmbeddedId 注解行使两个注解的功能;
  • 显然,第二种方案稍优于第一方案,至少可少使用一个注解;
  • 第三种方案代码有重复之处,与 OOP 中的重用原则相违背,请慎用。

4. 小结

本节课,聊到了主键生成器,通过主键生成器这个助攻手,能有效地保持主键的唯一性,从而保证数据的完整性。

另聊了复合主键,复合主键映射备选方案虽多,但你可只记你心中最钟情的那个。

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

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

相关文章

CentOS 7.9 64位安装nginx,部署tomcat实现负载均衡

在CentOS 7.9 中安装nginx做负载均衡步骤 用ubuntu试了一下没成功 centos更顺利&#xff0c;当初听到什么nginx&#xff0c;负载均衡&#xff0c;反向代理&#xff0c;redis这些就潜意识就觉得好复杂&#xff0c;今天花一下午时间尝试了一下&#xff0c;感觉也没那么难&#xf…

uniapp判断h5/微信小程序/app端+实战展示

文章目录 导文使用条件编译的基本语法常见的平台标识符示例实战展示使用场景举例注意事项 导文 这里是导文 当你在开发Uni-app时&#xff0c;需要根据不同的平台&#xff08;比如App端、H5端、微信小程序等&#xff09;来执行不同的代码逻辑&#xff0c;可以使用条件编译来实现…

深度解析:电商订单API及其技术实现

随着电子商务的发展&#xff0c;实体企业开拓电商渠道的越来越多&#xff0c;原有的管理系统都需要增加电商业务管理功能&#xff0c;其中&#xff0c;对电商订单的管理是每一个电商商家都需要的功能&#xff0c;所以对于开发者来说&#xff0c;了解电商API是什么是非常重要的&…

FastBee物联网开源项目本地启动调试

一、本地环境准备 &#xff08;1&#xff09;Visual Studio Code&#xff08;启动前端项目&#xff09; &#xff08;2&#xff09;IntelliJ IDEA Community Edition &#xff08;启动后端项目&#xff09; &#xff08;3&#xff09;Navicat或者DBeaver&#xff08;用来操…

去除重复字母

题目链接 去除重复字母 题目描述 注意点 s 由小写英文字母组成1 < s.length < 10^4需保证 返回结果的字典序最小&#xff08;要求不能打乱其他字符的相对位置&#xff09; 解答思路 本题与移掉 K 位数字类似&#xff0c;需要注意的是&#xff0c;并不是每个字母都能…

LinuxShell编程1———shell基础命令

文章目录 前言 一、shell基础知识 1、shell概念 2、Shell的功能 接收&#xff1a;用户命令 调用&#xff1a;相应的应用程序 解释并交给&#xff1a;内核去处理 返还&#xff1a;内核处理结果 3、Shell种类&#xff08;了解&#xff09; 3.1、MS-DOS 3.2、Windows的…

Microsoft Edge(简称Edge)

Microsoft Edge&#xff08;简称Edge&#xff09;是一款由微软开发的网页浏览器&#xff0c;它为用户提供了许多便捷的功能和选项。以下是Edge浏览器的使用方法&#xff1a; 一、基本使用方法 打开Edge浏览器&#xff1a; 可以在Windows的开始菜单中找到“Microsoft Edge”并点…

探索编程世界的乐趣:《C++青少年趣味编程108例》

&#x1f482; 个人网站:【 摸鱼游戏】【网址导航】【神级代码资源网站】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

Python学习:实现Python项目并学习如何进行(附70个项目源码)

实现Python项目并学习如何进行&#xff0c;是一个循序渐进的过程&#xff0c;涵盖了多个方面&#xff0c;包括基础知识的学习、技能的提升、项目的规划和实施等。以下是一个基本的指南&#xff0c;帮助你开始学习并实现Python项目&#xff1a; 1. 学习Python基础知识 语法与基…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第二篇 Linux系统编程篇-第三十三章 库的制作与使用

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

NDK R25b 交叉编译FFMpeg4,项目集成,附库下载地址

1.准备工作 文件下载&#xff1a; NDK R25b下载地址&#xff1a;Android NDK历史版本下载网址 - 君*邪 - 博客园 (cnblogs.com) FFmpeg4.4.4 下载地址&#xff1a;https://ffmpeg.org/releases/ffmpeg-4.4.4.tar.xz 环境配置&#xff1a; 本次编译环境是在PC虚拟机中使用U…

BigMarker-抽奖前置规则过滤

需求 在我们的流程设计中&#xff0c;用户执行抽奖时会判断是否已经超过N积分&#xff0c;如果超过N积分则可以在限定范围内进行抽奖。同时如果用户是黑名单范围的羊毛党用户&#xff0c;则只返回固定的奖品ID 模型 整个规则来说&#xff0c;分为抽奖前、抽奖中、抽奖后&#…

无人机之机型区别与应用领域

一、多旋翼无人机 特点&#xff1a;多旋翼无人机依靠产生升力以平衡飞行器的重力&#xff0c;通过改变每个旋翼的转速来控制飞行姿态&#xff0c;能够悬停和垂直起降。他们具备体积小、重量轻、噪音小、隐蔽性好的特点&#xff0c;操作灵活且易于维护。 应用&#xff1a;多旋…

数据库(创建数据库和表)

目录 一&#xff1a;创建数据库 二&#xff1a;创建表 2.1&#xff1a;创建employees表 2.2&#xff1a;创建orders表 2.3&#xff1a;创建invoices表 一&#xff1a;创建数据库 mysql> create database mydb6_product; Query OK, 1 row affected (0.01 sec) mysql&g…

排序——归并排序及排序章节总结

前面的文章中 我们详细介绍了排序的概念&#xff0c;插入排序&#xff0c;交换排序与选择排序&#xff0c;大家可以通过下面的链接再去学习&#xff1a; ​​​​​​排序的概念及插入排序 交换排序 选择排序 这篇文章就详细介绍一下另一种排序算法&#xff1a;归并排序以及…

鼠标宏编辑有什么作用?通用鼠标宏软件下载

你知道鼠标宏编辑吗&#xff1f;鼠标宏编辑是电脑鼠标连点器内一种常用功能。用户通过鼠标宏编辑可以很好提高效率。本文将深入探讨鼠标宏编辑的定义、作用及其在不同领域的应用&#xff0c;带您了解它的重要性和实际价值。并整理了2024年最新款的6大好用鼠标宏软件&#xff0c…

FPGA实训报告DAY 1(Verilog HDL)

实习日志与总结 日期&#xff1a;2024 年 7 月 10 日 星期三 姓名&#xff1a;XXX 一、实习日志 上午 9:00 - 9:30 按时到达工位&#xff0c;参加部门早会&#xff0c;了解了今天的实习任务和目标&#xff0c;即初步学习 FPGA 简介和 Verilog 基础语法知识。 9:30 - 10:30…

大数据基础:Doris重点架构原理

文章目录 Doris重点架构原理 一、Apache Doris介绍 二、Apache Doris使用场景 三、Apache Doris架构原理 四、Apache Doris 特点 Doris重点架构原理 一、Apache Doris介绍 基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff…

嵌入式人工智能(7-树莓派4B的IIC总线连接OLED显示中文与图片)

1、IIC总线 IIC总线&#xff08;Inter-Integrated Circuit&#xff09;是一种串行通信总线&#xff0c;也被称为I2C总线。它由飞利浦&#xff08;Philips&#xff09;公司在1980年代开发&#xff0c;用于连接微处理器和外部设备。 IIC总线使用两根信号线&#xff1a;SDA&…

【JavaScript 算法】树的遍历:前序、中序与后序

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、前序遍历&#xff08;Preorder Traversal&#xff09;前序遍历的步骤前序遍历的JavaScript实现 二、中序遍历&#xff08;Inorder Traversal&#xff09;中序遍历的步骤中序遍历的JavaScript实现 三、后序遍历&#xff…