簡介html
在前一篇博客 (https://www.javacodegeeks.com/2014/06/database-primary-key-flavors.html)中,咱們討論了UUID代理鍵(surrogate keys)以及比自增標示符更適用的使用場景。java
UUID的數據庫生成方式git
有不少種方式可表達一個128位UUID。每當遇到疑問時,我喜歡用stack exchange來做爲權威參考。github
因爲表ID通常會建索引,通過壓縮的數據類型會佔用更少的空間。按照效率由高到低有以下幾種選擇:數據庫
一部分數據庫(PostgreSQL, SQL Server) 提供了專門的UUID 存儲數據類型。數組
另外還可把若干bit存放到字節數組(好比Oracle 的RAW(16) 或標準類型的BINARY(16) )。session
或可用2個bigint列(64位),但這種複合列的ID的效率低於單一列。app
把16進制值存入一個char(36)列(其包含32個16進制數和4個」-「),但這會佔用不少磁盤空間,因此只是一個低效備選方案。dom
Hibernate提供了多種id策略供選擇,針對UUID有如下三種:ide
由應用程序邏輯來分配的uuid生成。
16進制的字符串uuid生成器。
uuid2生成器更靈活些。它能夠借用 java.lang.UUID ,這是16個字節數據或16進制字符串。
(程序)自定義生成方式
自定義生成方式可讓應用程序的邏輯來處理實體ID的生成過程。Hibernate經過簡單的忽略ID生成器定義來處理自定義ID。因爲數據庫使用了HSQLDB,因此下面示例使用BINARY(16) 列類型。
@Entity(name = "assignedIdentifier")
public static class AssignedIdentifier {
@Id
@Column(columnDefinition = "BINARY(16)")
private UUID uuid;
public AssignedIdentifier() {
}
public AssignedIdentifier(UUID uuid) {
this.uuid = uuid;
}
}
持久化實體:
session.persist(new AssignedIdentifier(UUID.randomUUID()));
session.flush();
具體生成一個INSERT語句:
Query:{[insert into assignedIdentifier (uuid) values (?)][[B@76b0f8c3]}
咱們能夠看看使用merge後會發生什麼:
session.merge(new AssignedIdentifier(UUID.randomUUID()));
session.flush();
此次既有select查詢又有insert:
Query:{[select assignedid0_.uuid as uuid1_0_0_ from assignedIdentifier assignedid0_ where assignedid0_.uuid=?][[B@23e9436c]}
Query:{[insert into assignedIdentifier (uuid) values (?)][[B@2b37d486]}
持久化方法會把transient實體添加到當前Hibernate session中。不過,若是session已有其餘實體或當前實體已被去除,就會拋出一個exception。若是須要,針對transient實體和已去除的實體,合併操做會複製當前對象的狀態到已持久化的實體中。但transient實體的持久化比合並要高效不少。
對於已生成的ID,因爲Hibernate並不能知道庫中有重複ID,因此合併還須要一次select查詢。對於其餘的ID生成器,Hibernate會找一空ID來判斷該實體有沒有處於transient狀態。所以,若是使用自定義ID,那麼Spring Data包裏面 SimpleJpaRepository#save(S entity) 方法並非最好選擇:
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
這個方法對於自定義ID會調用em.merge()而不是em.persist()。因此每次插入新實體都須要先select而後才能insert,效率天然會低些。
UUID生成器
此次咱們再也不用程序分配ID,而是讓Hibernate來生成。若是遇到空ID,Hibernate就認爲是transient實體,會給出一個新ID值。另外,merge方法也再也不須要先select查詢了。
UUIDHexGenerator
UUID hex generator 是最古老的UUID identifier 生成器,也是一種 「uuid」類型。可生成一個32位 16進制UUID字符串(也帶有分隔符),模式遵循:8{sep}8{sep}4{sep}8{sep}4。
這個生成器不受IETF RFC 4122 約束,使用該模式的數字表達式例如8-4-4-4-12。
@Entity(name = "uuidIdentifier")
public static class UUIDIdentifier {
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
@Column(columnDefinition = "CHAR(32)")
@Id
private String uuidHex;
}
持久化和合並一個transient 實體:
session.persist(new UUIDIdentifier());
session.flush();
session.merge(new UUIDIdentifier());
session.flush();
對每一個操做生成一個 INSERT 語句:
Query:{[insert into uuidIdentifier (uuidHex) values (?)][2c929c6646f02fda0146f02fdbfa0000]}
Query:{[insert into uuidIdentifier (uuidHex) values (?)][2c929c6646f02fda0146f02fdbfc0001]}
最好檢查傳給SQL INSERT和select的string參數值。
UUIDGenerator
最新的UUID generator受IETF RFC 4122約束 (variant 2),而且可提供插件式生成策略。註冊「uuid2″ 類型,提供更廣的類型供選擇。
java.lang.UUID
一個16字節的數組
一個16進制字符串值
@Entity(name = "uuid2Identifier")
public static class UUID2Identifier {
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(columnDefinition = "BINARY(16)")
@Id
private UUID uuid;
}
持久化和合並一個transient 實體的方法使用方式:
session.persist(new UUID2Identifier());
session.flush();
session.merge(new UUID2Identifier());
session.flush();
對每一個操做生成一個 INSERT 語句:
Query:{[insert into uuid2Identifier (uuid) values (?)][[B@68240bb]}
Query:{[insert into uuid2Identifier (uuid) values (?)][[B@577c3bfa]}
因爲咱們配置了@Id column定義,這個insert使用字節數組:
具體更多代碼可查看 GitHub (https://github.com/vladmihalcea/hibernate-master-class).