領先一步
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
來存取屬性或其擁有實體上的註解(包括 meta 註解)。 您可以使用此功能使用您自己的註解和 meta 註解來控制用於您的領域模型的資料庫類型。
例如,您可以建立一個註解層,指定資料庫層級類型,以及另一組使用第一個註解的領域特定註解,如以下程式碼片段所示:
@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 提供了一種靈活而強大的方式,可以從您的領域模型產生資料庫遷移。我們期待聽到您對此的意見和經驗。