数据库版本控制
为确保源代码中的数据模型与关系数据库匹配,保持它们同步是至关重要的。 应对这一挑战有两种常用方法。
数据库优先
在这种方法中,数据库优先于数据模型类(POJOs 或 JPA 实体),这些类通过代码生成从数据库模式生成,这也称为“数据库反向工程”。务必避免修改生成的类,因为它们可以随时重新生成,源代码中所做的任何更改都将丢失。 不过,这种方法 并不能消除生成迁移脚本的需要 ,因为它们对于将现有安装升级到最新版本是必要的。
源码优先
这是一种相反的方法,这里的数据模型类作为唯一的事实来源。 因此,数据库会因数据模型中的更改而被修改。 更新数据库时,迁移脚本必须表示过时数据库状态和数据模型类当前状态之间的更改,可以是任意格式,例如 Flyway SQL 迁移或 Liquibase 更改日志。
IntelliJ IDEA 提供方便的工具来帮助开发人员在这两种情况下继续进行。 本指南介绍了 IntelliJ IDEA 如何在差异更新脚本生成方面节省大量时间。
库支持
IntelliJ IDEA 支持常用于 Java 应用程序和 JPA 的两种最受欢迎的解决方案:
通用微分脚本生成流程
本节介绍迁移脚本生成的基本原理。 有关如何在 Liquibase 和 Flyway 中生成迁移脚本的具体细节,请参阅 Liquibase 和 Flyway。
要生成差异迁移脚本,请在 数据库 中右键点击数据库连接,然后选择 创建 Liquibase 更改日志 或 创建 Flyway 版本化迁移。 如果在您的构建文件中添加了 JPA 和 Liquibase 或 Flyway 依赖项,这些操作即可用。
在打开的对话框中,选择源(数据模型的理想状态)和目标(数据模型的旧状态)。
生成的迁移脚本 = 当前状态(源)- 之前状态(目标)。
IntelliJ IDEA 将生成迁移脚本以将目标数据库升级到源状态。
您可以在以下源选项之间进行选择:
DB — 应该在您有一个最新数据库并且希望生成迁移脚本以将另一个数据库更新到相同状态时使用。
Model — 使用它来生成迁移脚本,表示当前实体关系模型(JPA 实体)状态与旧(目标)状态之间的差异。
目标可以设置为:
DB — 具有旧版本模式的目标 DB。
Snapshot — 如果您在数据模型快照中存储了所需的状态,请使用此选项。 这也可以通过 JPA Buddy 生成。
点击确认以继续。 JPA Buddy 将分析 Source 和 Target 之间的差异,并显示预览对话框,以便对生成的迁移脚本进行微调。 点击 保存 以将新脚本添加到项目中或附加到现有脚本。
差异迁移脚本生成选项
使用数据库
如果您的源数据库已与您的数据模型同步,那么将一个数据库与另一个数据库或快照进行比较是有意义的。 有两种最受欢迎的的方法可以使数据库与 JPA 实体保持一致:
使用模式自动生成器(Hibernate 和 EclipseLink 提供了各自的实现)。 请注意,即使 Hibernate 文档也警告不要将这种方式用于原型设计或测试之外的原因。
手动在数据库架构上应用 JPA 实体的更改。 这种方法看起来可能过于繁琐,尤其是在数据模型经常变更的早期开发阶段。
使用数据模型
一个应用程序使用 JPA entities 来表示数据模型,包括实体、关联、索引和其他相关元素,遵循 JPA 原则。 换句话说,它已经包含了有关数据库模式的足够信息。 因此,您的源代码是唯一的真理点,代表了最新的(源)架构。 这就是为什么最好将您的数据模型与数据库/快照进行比较,以生成差异更改日志。
IntelliJ IDEA 扫描所有 JPA 对象,将它们与目标数据库或快照进行比较,并生成差异迁移脚本。
当数据模型用作当前架构状态的来源时,它会导致选择持久化单元。
根据文档 :持久化单元定义了应用程序中由 EntityManager 实例管理的所有实体类的集合。 这一组实体类表示单个数据存储中的数据。
这实际上意味着,如果您的应用程序使用多个数据存储,则需要分别为每个数据存储生成迁移脚本,指定相应的持久性单元。
使用数据模型快照
IntelliJ IDEA 允许使用数据模型快照作为比较的目标。 有时,获取某个模型状态的数据库是不可行或困难的,例如在将更改合并到某些早期应用版本时。 对于每个版本保留一个数据库转储可能不可行。 IntelliJ IDEA 允许您查看应用程序的所需版本,并基于 JPA 实体生成 JSON 快照,从而在生成差异迁移脚本时无需数据库。
生成数据模型快照
打开 Persistence 工具窗口。
右键点击一个元素并选择 。

这使您能够捕捉数据模型在某个较早时刻的状态,以便创建一个 diff migration script,描述从那时到现在发生的所有修改。
例如,您在功能分支中工作并修改了模型。 在合并之前,有必要创建一个仅描述此分支更改的差异更新日志。
根据具体配置,可能没有始终与主分支同步的 DB。 当您需要将更改合并到不是主分支的应用程序状态中时,例如发布分支,事情会变得更加复杂。 为每个版本维护数据库转储可能不切实际。 IntelliJ IDEA 提供一个更简单的选择:
检出目标分支(例如 main 或 release)
在该分支中创建模型的快照
请检查功能分支
通过比较模型和您在步骤 2 中创建的快照来生成差异迁移脚本。
只需四个简单步骤,您就能获得一个迁移脚本,该脚本描述当前分支与目标分支之间的更改。
预览窗口
Liquibase 的预览窗口如下所示(Flyway 的预览窗口略有不同):

某些类型的更改在预览窗口中具有自定义字段。 例如, 添加非空约束 更改允许您将数据库中所有现有的空值替换为指定的值:

每种更改类型根据其危险级别进行颜色编码:绿色表示安全,黄色表示警告,红色表示危险。 安全操作是指那些不会以任何方式导致数据丢失的操作,例如,添加列不会影响现有数据。 标记为 CAUTION 的操作通常是安全的,但需要您的注意:例如,如果列中存在空值,则添加 NOT NULL 约束可能会失败。 危险操作可能会导致数据丢失,例如删除列或修改数据类型。
可以在 中的 IDE 设置下自定义危险级别:

您可以配置每种更改类型的位置,或者是在主要位置,或者是在次要位置,或者完全忽略它。 默认情况下,新生成的迁移脚本将排除忽略的更改,但会在预览期间在 已忽略 部分中显示它们,以便可以手动添加回来。 对于 Liquibase,您还可以设置每种变更类型使用的上下文和标签。
合并语句
基本上,重命名模式元素,如表名、列名等,会导致两个语句:
删除一个现有值
新增一个
但是 JPA Buddy 可以用单个重命名或修改语句替换此类语句。 例如,在重命名列/表/序列或更改列类型后,您将在预览窗口中看到两条语句。 但是,通过选择任何相关语句,您可以将它们合并:
合并后,drop 语句可能不再相关。 您可以选择要从迁移脚本中删除的更改。 例如,在重命名 id 列(而不是删除旧值并添加新值)之后,您无需为其添加新的主键。

按实体生成 DDL
IntelliJ IDEA 允许您将实体转换为 DDL 语句。 您可以生成:
多个实体的初始化脚本 :基于整个模型或选定的实体创建数据库架构。
差异 DDL :根据 JPA 实体更新现有数据库至有效状态。
如果您希望避免使用由 hbm2ddl 或 ddl-auto 属性启用的自动脚本生成功能,此功能非常有用。 通过使用此功能,您可以在执行前完全控制 DDL,设置正确的 Java -> DB 类型映射,使用属性转换器和 Hibernate 类型映射字段,生成删除语句等。
为单个实体生成 DDL
IntelliJ IDEA 提供了一个操作,用于为特定实体生成 DDL 语句。
在编辑器中打开您的实体源代码后,点击
(显示实体操作 )位于边距中,并选择 生成 DDL。 或者将光标放在类名上,按 Alt+Enter 调出上下文操作,然后选择 生成 DDL。

或者,在 项目 工具窗口中,右键点击您的实体类文件并选择 。
在打开的 按 <Entity name> 生成 DDL 窗口中,配置保存 DDL 语句的选项:
数据库类型 :选择您希望为其生成 DDL 语句的数据库管理系统。
另存为 :选择如何保存 DDL 语句: 文件、 草稿文件、 剪贴板 或 数据库控制台。
目录 :如果 DDL 语句保存为文件,请选择文件位置。
文件名 :如果 DDL 语句保存为文件,请选择文件名。
数据库连接 :如果 DDL 语句保存为控制台命令,请选择您希望运行此语句的数据库连接。

预览语句并点击 OK 以保存它。
生成数据库初始化脚本
打开一个 SQL 文件或一个 数据库控制台。 在工具栏中,点击
。
在打开的 初始化 DDL 对话框中,点击 模型 并选择一个持久化单元。
要为特定实体生成 DDL 脚本,请在 范围 下选择 所选实体。

点击 OK。 IntelliJ IDEA 将分析 Source 和 Target 之间的差异,并显示 DDL 预览 对话框。
预览 DDL 语句并点击 保存 将生成的 DDL 脚本插入到您的数据库控制台或 SQL 文件中。

生成差异 DDL
差异 DDL 是一组 SQL 命令,用于基于 JPA 实体更新现有数据库:创建、修改、删除表、列、约束等。
打开一个 SQL 文件或一个 数据库控制台。 在工具栏中,点击
。
在打开的 差异 DDL 对话框中,选择源(数据模型的期望状态)和目标(数据模型的旧状态)。
您可以在以下源选项之间进行选择:
数据库 :应在您拥有最新数据库并希望生成迁移脚本以将另一个数据库更新到相同状态时使用。
模型 :使用它来生成迁移脚本,以表示当前实体关系模型(JPA 实体)与旧(目标)状态之间的差异。
目标可以设置为:
数据库 — 目标数据库的架构版本较旧。
快照 — 如果您想要的状态存储在数据模型快照中,请使用此选项。 它也可以由 IntelliJ IDEA 生成。

点击 OK。 IntelliJ IDEA 将分析 Source 和 Target 之间的差异,并显示 DDL 预览 对话框。

预览 DDL 语句并点击 保存 将生成的 DDL 脚本插入到您的数据库控制台或 SQL 文件中。
自动解决 SchemaManagementException
如果您在应用程序启动时遇到 SchemaManagementException ,这意味着您已将 ddl-auto 属性设置为 validate ,并且 Hibernate 无法将 JPA 实体正确映射到您的数据库表。 JPA Buddy 允许您生成 DDL 以填补 JPA 实体和数据库之间的差异,直接从堆栈跟踪开始!
SQL 可视化设计器
在某些情况下,拥有 JPA 数据模型的 SQL 脚本很有用,尤其是在您需要快速建立新的数据库时。 JPA Buddy 可以通过 JPA Palette 或 Editor Toolbar 生成各种 SQL 语句。 对于每个语句,都有一个相应的窗口允许您配置该语句:
Hibernate 6 支持
在 Hibernate 6 之前,通常使用 Hibernate Types 库或 @Type/@Converter 注解将非标准 SQL 类型映射到 Java 类型。 Hibernate 6 提供了新的映射注解,可以消除大量 Hibernate 类型和 JPA converters。 目前,JPA Buddy 支持这些新的映射注解:
@JdbcType,@JdbcTypeCode&@JdbcTypeRegistration
@Type,@JavaType&@JavaTypeRegistration
@TimeZoneStorage&@TimeZoneColumn
Hibernate Envers 支持
Hibernate Envers 是一个有助于在数据库中进行实体审计的模块。 JPA Buddy 可以生成所有必需的表,以便 Hibernate Envers 能够正常运行。 这包括带有 @Audited 注解的实体的审计表和带有 @RevisionEntity 注解的实体的修订表。 看看它是如何实际运行的:
JPA Buddy 为 Hibernate Envers 提供灵活的设置。 如需详细信息,请参阅 相应部分。
设置
自定义类型映射
没有通用方法可以自动将自定义 Java 类型映射到 SQL/Liquibase 类型。 这就是为什么您需要为这些属性手动定义目标类型。 如果您的项目中存在这样的属性,在调用 Liquibase 或 Flyway 脚本生成操作后,JPA Buddy 将显示以下窗口:

您可以随时从 更改已保存的映射配置。
此外,这在应用程序处理来自不同供应商的数据库时可能会有所帮助。 在这种情况下,您的模式可能对每个数据类型稍有不同。
假设该应用程序需要支持 PostgreSQL 和 MS SQL。 并且您希望在字符串中存储 Unicode 字符。 PostgreSQL 支持 Unicode 字符 VARCHAR ,但 MS SQL 有一个单独的数据类型 NVARCHAR。
您可以为每个 DBMS 指定类型映射。 还可以为 JPA 转换器和 Hibernate 类型设置映射:

转换器
为了简化类型映射,IntelliJ IDEA 引入了与数据库无关的 SQL 类型,这些类型被转换为数据库特定的类型。 例如,“varchar” 在 Oracle 数据库中会转化为 “varchar2”,在 PostgreSQL 中则保持为 “varchar”。
每个 DB-agnostic 类型都有一组别名(例如,“java.sql.Types.VARCHAR”或“character varying”),在大多数情况下是可以互换的。 未知类型将按原样使用,不进行任何转换。 查看表格中的全部别名列表:
名称 | 別名 | Liquibase 类 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Hibernate Envers

Hibernate Envers 提供多种自定义选项。 您可以在 JPA Buddy 设置(2)中指定所需的配置,或让 JPA Buddy 自动从 .属性 文件中读取现有设置(1)。
命名策略
默认情况下,Spring Boot 使用 SpringPhysicalNamingStrategy 配置物理命名策略。 此实现生成的所有表名均为小写并使用下划线分隔。 例如, TelephoneNumber 实体映射到 telephone_number 表。 即使您对实体加了注释 @Table(name = "TelephoneNumber")。 迁移脚本必须使用相同的名称,因此 JPA Buddy 在脚本生成期间也会对所有名称应用物理命名策略。
支持以下策略:
SpringPhysicalNamingStrategy——默认选项PhysicalNamingStrategyStandardlmplCamelCaseToUnderscoresNamingStrategy(仅适用于 Hibernate 6 及更高版本的项目)
要了解更多关于命名策略的信息,您可以查看我们的 文章。
自定义命名策略
有时,项目中命名策略的默认行为会发生变化。 JPA Buddy 也可以帮助您! 一旦它检测到项目中的一个类实现了 org.hibernate.boot.model.naming.PhysicalNamingStrategy (或它的一个子类),带有相应类的额外“custom”部分将出现在命名策略下拉菜单中。

一旦从下拉菜单中选择此类,IntelliJ IDEA 将在生成迁移脚本时使用此策略。 请注意,IntelliJ IDEA 不会自动更新类的变更,因此如果您修改策略代码,您需要点击 重新加载自定义物理命名策略类 或重新启动 IntelliJ IDEA。

最大标识符
关系数据库管理系统有其自身的限制。 例如,12.1 版本之前的 OracleDatabase 表名限制为 30 字节。 为了避免版本控制脚本出现问题,您可以限制表名称:

此外,您可以定义:
是否为关联外键约束创建索引;
主键约束名称.