領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多這是關於如何解決在使用 Spring Data JDBC 時可能遇到的各種挑戰的系列文章的第五篇。該系列文章包括
Spring Data JDBC - 如何為我的域模型生成架構?(本文)
如果您是 Spring Data JDBC 的新手,建議您先閱讀其簡介和這篇解釋聚合在 Spring Data JDBC 上下文中的相關性的文章,以理解基本概念。
使用任何物件關係對映器 (ORM),您都必須建立兩樣東西,並且它們必須相互匹配:
從 3.2.0-M1 版本開始,Spring Data Relational 將幫助您完成此操作。本文將向您展示如何使其工作。
首先要做的是找到一個放置模式生成程式碼的地方。我們建議為此使用測試。您可以從它那裡使用主應用程式的配置,並且它不會在生產環境中意外執行。
接下來要做的是獲取一個 RelationalMappingContext。這是 Spring Data Relational 的核心類,它是 Spring Data JDBC 和 Spring Data R2DBC 的父級。一旦完全初始化,這個類將儲存所有關於您的聚合的對映元資訊。但是這個初始化是惰性發生的,所以您必須自己註冊您的聚合根。
然後,您需要從它建立一個 LiquibaseChangeSetWriter,並使用它來寫入 Liquibase 變更集。
// context is a RelationalMappingContext that you autowire in your test.
context.setInitialEntitySet(Collections.singleton(Minion.class));
LiquibaseChangeSetWriter writer = new LiquibaseChangeSetWriter(context);
writer.writeChangeSet(new FileSystemResource("cs-minimum.yaml"));
為此,您需要在依賴中新增 Liquibase:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
注意:如果您使用 Spring Boot,Liquibase 依賴將觸發使用 Liquibase 的模式初始化,但會失敗,因為它找不到任何變更集。您可以透過在您的 application.properties 中新增此行來輕鬆停用它。
spring.liquibase.enabled=false
如果執行此測試,您應該在專案根資料夾中找到一個名為 cs-minimum.yaml 的檔案:
databaseChangeLog:
- changeSet:
id: '1692728224754'
author: Spring Data Relational
objectQuotingStrategy: LEGACY
changes:
- createTable:
columns:
- column:
autoIncrement: true
constraints:
nullable: true
primaryKey: true
name: id
type: BIGINT
- column:
constraints:
nullable: true
name: name
type: VARCHAR(255 BYTE)
tableName: minion
您應該檢視此檔案,根據需要修改它,並將其放置在 Liquibase 可以拾取的位置。如果您之前已停用它,現在請啟用 Liquibase 的模式初始化以實際使用此變更集。
對於您的應用程式的第二個版本,您可能需要對資料庫模式進行一些更新。Spring Data JDBC 也可以幫助您完成這些工作。
為了建立這種增量模式更新,我們需要提供資料庫的當前狀態。這透過 liquibase.database.Database 例項完成,您可以從 DataSource 建立它。
@Autowired
DataSource ds;
// ...
context.setInitialEntitySet(Collections.singleton(Minion.class));
LiquibaseChangeSetWriter writer = new LiquibaseChangeSetWriter(context);
try (Database db = new HsqlDatabase()) {
db.setConnection(new JdbcConnection(ds.getConnection()));
writer.writeChangeSet(new FileSystemResource("cs-diff.yaml"), db);
} catch (IOException | SQLException | LiquibaseException e) {
throw new RuntimeException("Changeset generation failed", e);
}
上面的例子使用了 HsqlDatabase。您將使用與您的實際資料庫匹配的實現。
預設情況下,變更集從不從您的模式中刪除列或表。僅僅因為它們沒有在領域模型中建模,並不意味著您不需要它們,對嗎?但是,如果您確實想刪除 Java 領域模型中不存在的部分或所有表和列,請註冊一個 DropTableFilter 或 DropColumnFilter,如下面的示例所示,它會刪除所有未對映的列,除了名為 special 的列。
writer.setDropColumnFilter((table, column) -> !column.equalsIgnoreCase("special"));
Spring Data JDBC 沒有用於為列指定確切資料庫型別的註解。但它提供了一個使用您想要的型別的鉤子。您可以向 LiquibaseChangeSetWriter 提供一個 SqlTypeMapping。
writer.setSqlTypeMapping(((SqlTypeMapping) property -> {
if (property.getName().equalsIgnoreCase("name")) {
return "VARCHAR(500)";
}
return null;
}).and(new DefaultSqlTypeMapping()));
您只需實現該介面的一個方法:String getColumnType(RelationalPersistentProperty property)。在您只想修改某些情況下的型別時,您可以將其與 DefaultSqlTypeMapping 結合使用,後者將用於您的實現返回 null 的所有情況,如示例所示。
RelationalPersistentProperty 包含一些非常有用的方法,例如 findAnnotation 用於訪問屬性或其擁有實體上的註解(包括元註解)。您可以使用此功能來使用您自己的註解和元註解來控制用於您的領域模型的資料庫型別。
例如,您可以建立一層註解來指定資料庫級別的型別,並使用第一層註解建立另一組特定於領域的註解,如以下程式碼片段所示:
@Retention(RetentionPolicy.RUNTIME)
public @interface Varchar {
/**
* the size of the varchar.
*/
int value();
}
@Varchar(20)
@Retention(RetentionPolicy.RUNTIME)
public @interface Name {
}
然後,您可以使用此註解來註解您的領域模型中的屬性,並使用匹配的 SqlTypeMapping。
@Name
String name;
writer.setSqlTypeMapping(((SqlTypeMapping) property -> {
if (!property.getType().equals(String.class)) {
return null;
}
// findAnnotation will find meta annotations
Varchar varchar = property.findAnnotation(Varchar.class);
int value = varchar.value();
if (varchar == null) {
return null;
}
return "VARCHAR(" +
varchar.value() +
")";
}).and(new DefaultSqlTypeMapping()));
模式生成目前不支援引用。這些將被靜默忽略。當然,我們將來會改進這一點。
如果您來自 JPA/Hibernate,您習慣於透過簡單的配置直接在資料庫中生成模式,並且將模式資訊作為對映註解的一部分。很自然會問我們為什麼選擇不同的方式。
這個問題有多個答案:
您很容易做一些只能透過應用資料庫備份才能恢復的操作。我們認為讓開發人員在不真正看到,更不用說思考他們所應用的更改的情況下做這種事情是不好的。這就是為什麼我們建立更改,但將應用更改作為單獨的步驟。
這就是我們選擇 Liquibase 來建立和管理更改的原因。
因此,這類資訊不應作為 Spring Data JDBC 使用的對映註解的一部分。相反,這類資訊應以真正獨立於 Spring Data JDBC 的方式從您的模型中派生出來。我們認為所示的元註解方法是實現這一目標的好方法。
憑藉當前的里程碑和即將釋出的 GA 版本,Spring Data JDBC 提供了一種靈活而強大的方法,可以從您的領域模型生成資料庫遷移。我們期待聽到您對此的意見和經驗。