使用 Hibernate 生成 UUID 作為主鍵
一、簡介
UUID 是數據庫中使用的一種相對常見的主鍵類型。它實際上是全局唯一的,這使得它成為分佈式系統中 ID 類型的不錯選擇。
在本教程中,我們將了解如何利用 Hibernate 和 JPA 為我們的實體生成 UUID。
2. JPA/雅加達規範
首先,我們將看看 JPA 提供了什麼來解決這個問題。
自 2022 年發布的 3.1.0 版本以來,JPA 規範為開發人員提供了一個新的GenerationType.UUID
,我們可以在@GeneratedValue
註解中使用它:
@Entity
class Reservation {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String status;
private String number;
// getters and setters
}
GenerationType
指示實體的 UUID 應由持久性提供程序自動為我們生成。
特別是 Hibernate,從版本 6.2 開始支持 JPA 3.1.0。所以,至少有 Hibernate 6.2,這會起作用:
@Test
public void whenGeneratingUUIDUsingNewJPAGenerationType_thenHibernateGeneratedUUID() throws IOException {
Reservation reservation = new Reservation();
reservation.setStatus("created");
reservation.setNumber("12345");
UUID saved = (UUID) session.save(reservation);
Assertions.assertThat(saved).isNotNull();
}
但是,在RFC 4122 中,定義了四種類型/版本的 UUID。 JPA 規範將 UUID 版本的選擇留給了持久性提供者。所以不同的持久性提供者可能會生成不同版本的 UUID 。
默認情況下,Hibernate 生成第 4 版的 UUID:
@Test
public void whenGeneratingUUIDUsingNewJPAGenerationType_thenHibernateGeneratedUUIDOfVersion4() throws IOException {
Reservation reservation = new Reservation();
reservation.setStatus("new");
reservation.setNumber("012");
UUID saved = (UUID) session.save(reservation);
Assertions.assertThat(saved).isNotNull();
Assertions.assertThat(saved.version()).isEqualTo(4);
}
根據RFC 4122 ,Hibernate 能夠創建兩個版本的 UUID——1 和 4。稍後我們將看到如何生成基於時間的(版本 1)UUID。
3. 在 Hibernate 6.2 之前
在某些項目中,可能無法從 JPA 規範 2.x 跳轉到 JPA(或 Jakarta)規範 3.1.0。但是,如果我們有 Hibernate 版本 4 或 5,我們仍然可以生成 UUID。為此,我們有兩種方法。
首先,我們可以通過在@GenericGenerator
註釋中指定org.hibernate.id.UUIDGenerator
類來實現:
@Entity
class Sale {
@Id
@GeneratedValue(generator = "uuid-hibernate-generator")
@GenericGenerator(name = "uuid-hibernate-generator", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
private boolean completed;
//getters and setters
}
行為將與 Hibernate 6.2 中的行為相同:
@Test
public void whenGeneratingUUIDUsingGenericConverter_thenAlsoGetUUIDGeneratedVersion4() throws IOException {
Sale sale = new Sale();
sale.setCompleted(true);
UUID saved = (UUID) session.save(sale);
Assertions.assertThat(saved).isNotNull();
Assertions.assertThat(saved.version()).isEqualTo(4);
}
然而,這種方法非常冗長,我們可以通過使用org.hibernate.annotations.UuidGenerator
註釋獲得相同的行為:
@Entity
class Sale {
@Id
@UuidGenerator
private UUID id;
private boolean completed;
// getters and setters
}
此外,在指定@UuidGenerator
時,我們可以選擇要生成的UUID 的具體版本。這是由style
參數定義的。讓我們看看這個參數可以取的值:
-
RANDOM
– 根據隨機數生成 UUID(RFC 中的版本 4) -
TIME
生成基於時間的 UUID(RFC 中的版本 1) -
AUTO
– 這是默認選項,與RANDOM
相同
讓我們看看如何控制 Hibernate 生成的 UUID 的版本:
@Entity
class WebSiteUser {
@Id
@UuidGenerator(style = UuidGenerator.Style.TIME)
private UUID id;
private LocalDate registrationDate;
// getters and setters
}
現在,正如我們可以檢查的那樣,Hibernate 將生成基於時間的(版本 1)UUID:
@Test
public void whenGeneratingTimeBasedUUID_thenUUIDGeneratedVersion1() throws IOException {
WebSiteUser user = new WebSiteUser();
user.setRegistrationDate(LocalDate.now());
UUID saved = (UUID) session.save(user);
Assertions.assertThat(saved).isNotNull();
Assertions.assertThat(saved.version()).isEqualTo(1);
}
4. String
作為UUID
此外,如果我們使用String
作為 Java ID 類型,Hibernate 足夠智能,可以為我們生成 UUID:
@Entity
class Element {
@Id
@UuidGenerator
private String id;
private String name;
}
正如我們所見,Hibernate 可以處理String
和UUID
Java 類型:
@Test
public void whenGeneratingUUIDAsString_thenUUIDGeneratedVersion1() throws IOException {
Element element = new Element();
element.setName("a");
String saved = (String) session.save(element);
Assertions.assertThat(saved).isNotEmpty();
Assertions.assertThat(UUID.fromString(saved).version()).isEqualTo(4);
}
在這裡,我們應該注意,當我們將列設置為java.util.UUID
類型時,Hibernate 會嘗試將其映射到數據庫中相應的 UUID 類型。這種類型可能因一個數據庫而異。
因此,確切的類型實際上取決於 Hibernate 方言集。例如,如果我們使用 PostgreSQL,那麼對應的類型將是 PostgreSQL 中的UUID
。如果我們使用的是 Microsoft SQL Server,那麼相應的類型將是UNIQUEIDENTIFIER
。但是,如果我們使用String
作為 Java ID 類型,那麼 Hibernate 會將其映射到某種 SQL 文本類型,例如TEXT
或VARCHAR
。
5.結論
在本文中,我們了解了使用 Hibernate 生成 UUID 的不同方法。
在 Jakarta 3.1.0 規範和 Hibernate 6.2 之前,有幾個選項可以做到這一點。從 Hibernate 6.2 開始,我們可以使用新的 JPA GenerationType.UUID
獨立於持久性提供程序生成 UUID。
但是,JPA 規範沒有指定生成的 UUID 的版本。如果我們想要指定一個具體的版本,我們需要使用特定於 Hibernate 的類,我們還有兩個選擇——版本 1 或版本 4。
與往常一樣,本教程的源代碼可 在 GitHub 上獲得。