Java EE 專案持久層雖隨應用伺服器異有不同持久化引擎實現,但 JPA 規範統一了主鍵生成策略。掌握核心策略,即可從容應對各類具體場景。
持久化引擎使用關係資料庫表生成主鍵,所有資料庫均支援。
自定義表生成器:
@Entity
public class PrimaryKey_Table {
@TableGenerator(
name = "PK_SEQ",
table = "SEQUENCE_TABLE",
pkColumnName = "SEQUENCE_NAME",
valueColumnName = "SEQUENCE_COUNT"
)
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "PK_SEQ")
private Long id;
// Getters/Setters
}
name:生成器名稱 (PK_SEQ),供 generator 引用。table:儲存主鍵的表 (SEQUENCE_TABLE),資料庫需存在。pkColumnName:該表主鍵欄位名。valueColumnName:儲存最後生成值的欄位。使用持久化引擎預設表:
@Id @GeneratedValue(strategy = GenerationType.TABLE) private Long id;
GlassFish 預設:table=SEQUENCE、pkColumnName=SEQ_NAME、valueColumnName=SEQ_COUNT。不同引擎預設值不同。
調用資料庫內建 Sequence 機制。
public class PK_Sequence implements Serializable {
@SequenceGenerator(name = "PK_SEQ_TBL", sequenceName = "PK_SEQ_NAME")
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PK_SEQ_TBL")
private Long id;
}
@SequenceGenerator 關鍵屬性:
name:生成器名。sequenceName:資料庫 Sequence 名。initialValue:初始值 (預設 1)。allocationSize:持久化引擎從 Sequence 讀取值的快取大小 (預設 50),效能優化關鍵。資料庫自增欄生成主鍵,僅需指定策略:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
引擎從 Table/Sequence/Identity 三者選擇。GlassFish 採 Table。顯式或隱式均可:
@GeneratedValue(strategy = GenerationType.AUTO) // 或 @GeneratedValue // 或什麼都不寫(預設即 AUTO)
實務常遇歷史表複合主鍵。JPA 2.0 要求:主鍵欄位置於獨立主鍵類,重寫 equals()/hashCode()、實作 Serializable、無參建構子。
主鍵類標註 @Embeddable:
@Embeddable
public class NewsId implements Serializable {
private String title;
private String language;
// getters/setters
@Override public boolean equals(Object obj) { /* 比較 title + language */ }
@Override public int hashCode() { /* 組合雜湊 */ }
}
實體類直接嵌入:
@Entity
public class News implements Serializable {
@EmbeddedId
private NewsId id;
private String content;
// Getters/Setters
}
生成表結構:PRIMARY KEY (TITLE, LANGUAGE)。
查詢寫法:SELECT n.id.title FROM News n。
主鍵類不需 @Embeddable,其餘要求相同(去掉註解即可)。
實體類宣告 @IdClass 並重複定義主鍵欄位且逐一標註 @Id:
@Entity
@IdClass(NewsId.class)
public class News implements Serializable {
@Id private String title;
@Id private String language;
private String content;
// Getters/Setters
}
生成表結構相同。查詢寫法:SELECT n.title FROM News n(較簡潔)。
注意:重複欄位定義必須與主鍵類完全一致。
| 策略 | 適用場景 | 可移植性 | 效能備註 |
|---|---|---|---|
| Table | 跨資料庫部署、無原生 Sequence/Identity | 最高 | 需額外表查詢/更新,可透過 allocationSize 優化 |
| Sequence | Oracle、PostgreSQL、DB2 等支援 Sequence | 高 | allocationSize 預設 50 減少資料庫往返 |
| Identity | MySQL、SQL Server、DB2 自增欄 | 中 | 插入後才知主鍵值,批次插入受限 |
| AUTO | 不想綁定具體策略、交由引擎決定 | 最高 | 實際行為視引擎而異 |
複合主鍵:@EmbeddedId 語義清晰(主鍵即值物件),@IdClass 查詢稍簡潔且符合某些習慣。根據團隊偏好與既有代碼風格選擇。
掌握上述策略,持久層開發即能「以不變應萬變」。