JPA 實體在持久化上下文中經歷四種狀態轉換,`EntityManager` 提供完整 API 控制生命週期。本文系統梳理狀態機、核心方法語義與級聯行為。
新建態 | persist() v 受控態 <---------- merge() -------- 游離態 | remove() find() / query() v 刪除態
| 狀態 | 特徵 | 持久化上下文關聯 |
|---|---|---|
| 新建態 | 剛 `new`,無 ID,未關聯 | 無 |
| 受控態 | 有 ID,已關聯,變更自動同步 | 有 |
| 游離態 | 有 ID,曾受控但已脫離 | 無 |
| 刪除態 | 已標記刪除,提交時從 DB 移除 | 有 (直到提交) |
| 方法 | 狀態轉換 | 關鍵語義 |
|---|---|---|
| `persist(entity)` | 新建 → 受控 | 事務提交時 INSERT;已受控無效果;刪除態→受控;游離態拋異常 |
| `remove(entity)` | 受控 → 刪除 | 事務提交時 DELETE;級聯刪除 (cascade=REMOVE/ALL);新建/刪除態忽略;游離態拋異常 |
| `merge(entity)` | 游離 → 受控 | 返回新受控實例;複製狀態至上下文;新建態視同 persist |
| `find(Class, pk)` | — → 受控 | 主鍵查詢,返回受控實例;未找到返回 null |
| `flush()` | 受控 → DB 同步 | 強制同步變更至資料庫,不提交事務 |
| `createQuery(jpql)` | — → Query | JPQL 查詢,返回 `Query` 執行 |
| `createNativeQuery(sql)` | — → Query | 原生 SQL,結果為 `List |
// 新建
Account acc = new Account("user1");
em.persist(acc); // 受控
em.flush(); // 同步取得生成 ID
// 查詢 (新事務/新 EntityManager)
Account found = em.find(Account.class, acc.getId()); // 受控
// 修改
found.setEmail("new@mail.com"); // 自動髒檢查,提交時 UPDATE
// 刪除
em.remove(found); // 刪除態
em.getTransaction().commit(); // 真正 DELETE
掌握狀態機與 API 語義,是寫出正確、高效 JPA 程式碼的關鍵。