在 Hibernate 中正確映射 MySQL AUTO_INCREMENT 主鍵,關鍵在於選擇適當的識別符產生策略(<generator>)。本文詳細解析 native、assigned、identity 等策略差異,提供完整映射檔案與程式碼範例。
CREATE TABLE `anietest`.`user` (
`id` INT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE UNIQUE INDEX `PRIMARY` ON `anietest`.`user`(`id`);
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping>
<class name="org.nieweiguo.User" table="user" catalog="anietest">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="native" />
</id>
<property name="username" type="java.lang.String">
<column name="username" length="50" />
</property>
</class>
</hibernate-mapping>
| 策略 | 適用資料庫 | 行為 | Hibernate 版本 | 備註 |
|---|---|---|---|---|
native | MySQL, PostgreSQL, Oracle, SQL Server 等 | 自動選擇 identity (MySQL/SQL Server) 或 sequence (Oracle/PostgreSQL) | 全版本 | 跨資料庫首選,MySQL 下等同 identity |
identity | MySQL, SQL Server, DB2, Sybase | 依賴資料庫自增欄位,INSERT 後立即取回生成值 | 全版本 | 不支援批次插入優化 |
assigned | 所有 | 由應用程式手動指定 ID 值 | 全版本 | 需在 save() 前設定 id,適用業務主鍵 |
increment | 所有 | Hibernate 記憶體內計數器產生 ID (單進程) | 全版本 | 叢集/多實例環境禁用,會產生重複 ID |
uuid/uuid2 | 所有 | 產生 UUID 字串 | 3.2+ | 分散式系統適用,需 CHAR(36) 儲存 |
// Action / Service 層
UserForm userForm = (UserForm) form;
UserDAO userDao = new UserDAO();
User user = new User();
// user.setId(0); // 不需設定,Hibernate 交由資料庫自增
user.setUsername(userForm.getUsername());
userDao.save(user); // INSERT 後 user.id 自動填入生成值
<generator class="assigned" />
// 程式碼必須手動設定 ID
User user = new User();
user.setId(0); // 或任何未使用的值,但通常由業務邏輯決定
user.setUsername(userForm.getUsername());
userDao.save(user);
關鍵差異:assigned要求應用層保證 ID 唯一性,native則交由資料庫保證。MySQLAUTO_INCREMENT配合native是最簡單、最安全的組合。
@Entity
@Table(name = "user", catalog = "anietest")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // MySQL 對應 native
@Column(name = "id")
private Integer id;
@Column(name = "username", length = 50)
private String username;
// getter/setter
}
GenerationType.IDENTITY 對應 MySQL AUTO_INCREMENT,GenerationType.AUTO 對應 native(由 JPA 提供者決定)。
原因:generator 設錯(如用 increment 但未 flush)、資料表無 AUTO_INCREMENT、交易未提交。
解決:確認映射檔 native、資料表定義正確、session.flush() 或交易提交後取值。
identity/native 需逐筆 INSERT 取回 ID,無法使用 JDBC 批次。
解決:大量匯入時暫時改用 assigned + 應用層產生 ID(如雪花算法),或使用 LOAD DATA INFILE 繞過 ORM。
資料表 BIGINT 實體用 Integer 會溢位。
解決:實體用 Long 對應 BIGINT,Integer 對應 INT。
MySQL 自增主鍵映射,首選 native (或 JPA IDENTITY),零配置跨資料庫相容、資料庫保證唯一、程式碼最簡潔。僅在業務主鍵、分散式 ID、批次匯入等特殊場景考慮 assigned、uuid 等策略。映射檔與實體類型、資料表定義三者嚴格對齊,是避免主鍵問題的基石。