在使用 JPA @TableGenerator 時設置 initialValue=10,預期主鍵從 11 開始生成,實際卻報主鍵衝突從 1 開始。根源在於 Hibernate 對 TableGenerator 有新舊兩種實現,默認使用舊版,導致 JPA 標準參數失效。
| 策略 | 適用數據庫 | 特點 | 性能影響 |
|---|---|---|---|
| AUTO | 通用 | 由持久化提供者選擇 | 不可控 |
| IDENTITY | MySQL, SQL Server, DB2 | 插入後生成主鍵 | 阻礙批量插入優化 |
| SEQUENCE | Oracle, PostgreSQL | 預分配序列號段 | 高性能,需數據庫支持 |
| TABLE | 通用 | 模擬序列,通用性最強 | 可優化,推薦新生成器 |
對不支持 SEQUENCE 的數據庫,TABLE 是唯一兼顧性能與通用性的選擇。
<property name="hibernate.id.new_generator_mappings" value="true"/>
Hibernate 官方建議:所有新項目必須啟用,新生成器更高效、更符合 JPA 2 標準。舊項目升級需謹慎,因主鍵生成邏輯不兼容可能導致衝突。
新生成器支持 8 個配置參數,分為兩組:
| 參數 | 對應常量 | 說明 | 默認值 |
|---|---|---|---|
| table_name | TABLE_NAME | 輔助表名 | hibernate_sequences |
| value_column_name | VALUE_COLUMN_NAME | 值列名 | next_val |
| segment_column_name | SEGMENT_COLUMN_NAME | 序列名列名 | sequence_name |
| segment_value | SEGMENT_VALUE | 當前序列名 | default |
| segment_value_length | SEGMENT_VALUE_LENGTH | 序列名列長度 | 255 |
支持單表存儲多個序列(segment),避免創建大量輔助表。
| 參數 | 對應常量 | 說明 | 默認值 |
|---|---|---|---|
| initial_value | INITIAL_VALUE | 初始值 | 1 |
| increment_size | INCREMENT_SIZE | 步長/號段大小 | 1 |
| optimizer | OPTIMIZER | 優化策略 | 見下文 |
| 優化器 | 輔助表存儲值 | 特點 |
|---|---|---|
| none | 下一個可用值 | 無優化,每次訪問數據庫 |
| hilo | 桶號 | 高低位算法,值 = bucket * increment + offset |
| legacy-hilo | 桶號 | 舊版 hilo 兼容 |
| pooled | 下下桶起始值 | 預分配號段,值 = table_value - increment |
| pooled-lo | 下一桶起始值 | 預分配號段,值 = table_value,更直觀 |
if (increment_size == 1) → none
else if (prefer_lo) → pooled-lo
else → pooled
默認 prefer_lo = false,故大多數情況選 pooled。
假設 increment_size=50,當前分配到主鍵 60:
| 優化器 | 輔助表值 | 含義 |
|---|---|---|
| hilo | 1 | 桶號 1(對應 51-100) |
| pooled-lo | 101 | 下一桶起始值 |
| pooled | 151 | 下下桶起始值 |
建議:設置 hibernate.id.optimizer.pooled.prefer_lo=true 使用 pooled-lo,輔助表值更符合直覺。
重啟應用導致未用完的號段丟失(如 61-100 永不使用)。這是正常現象,主鍵唯一性不要求連續性,無需焦慮。數據庫主鍵空洞不影響業務邏輯。
@Entity
@Table(name="emp3")
public class Employee3 {
@TableGenerator(
name="id_gen",
table="id_gen",
initialValue=10,
allocationSize=50 // 對應 increment_size
)
@Id
@GeneratedValue(strategy=TABLE, generator="id_gen")
private long id;
...
}
@Entity
@Table(name="emp4")
public class Employee4 {
@GenericGenerator(
name="id_gen",
strategy="enhanced-table",
parameters = {
@Parameter(name="table_name", value="enhanced_gen"),
@Parameter(name="value_column_name", value="next_val"),
@Parameter(name="segment_column_name", value="sequence_name"),
@Parameter(name="segment_value", value="emp_seq"),
@Parameter(name="increment_size", value="10"),
@Parameter(name="optimizer", value="pooled-lo")
}
)
@Id
@GeneratedValue(generator="id_gen")
private long id;
...
}
新生成器名稱在 DefaultIdentifierGeneratorFactory 中註冊為 "enhanced-table"。
hibernate.id.new_generator_mappings=trueoptimizer=pooled-lo + prefer_lo=trueHibernate 新 TableGenerator 解決了舊版不符合 JPA 標準、性能差、配置受限等問題。通過優化器機制(pooled/pooled-lo)實現號段預分配,大幅減少數據庫訪問。正確配置初始值、步長、優化策略,能構建高性能、標準兼容的主鍵生成方案。