Java Persistence API (JPA) 雖是 Java EE 規範,但 完全可以在 Java SE 環境獨立運行,無需 Web 容器。本文基於 NetBeans 8.1 自動生成的 Swing 桌面應用,深度剖析 JPA 在桌面端的實戰代碼結構、核心 API 使用與事務管理模式。
user (id, usernmae, password, name)JpaExampleuser 表 → 勾選顯示字段<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="https://xmlns.jcp.org/xml/ns/persistence">
<persistence-unit name="ccPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>com.cc.jpa.User</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/cc"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="123456"/>
<!-- EclipseLink 特有配置 -->
<property name="eclipselink.ddl-generation" value="none"/>
<property name="eclipselink.logging.level" value="FINE"/>
</properties>
</persistence-unit>
</persistence>
transaction-type="RESOURCE_LOCAL":Java SE 環境本地事務,非 JTAprovider:EclipseLink (JPA 2.1 參考實現,原 TopLink)@Entity
@Table(name = "user", catalog = "cc", schema = "")
@NamedQueries({
@NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
@NamedQuery(name = "User.findById", query = "SELECT u FROM User u WHERE u.id = :id"),
@NamedQuery(name = "User.findByUsernmae", query = "SELECT u FROM User u WHERE u.usernmae = :usernmae"),
@NamedQuery(name = "User.findByPassword", query = "SELECT u FROM User u WHERE u.password = :password"),
@NamedQuery(name = "User.findByName", query = "SELECT u FROM User u WHERE u.name = :name")
})
public class User implements Serializable {
@Id
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Basic(optional = false)
@Column(name = "usernmae")
private String usernmae;
@Basic(optional = false)
@Column(name = "password")
private String password;
@Basic(optional = false)
@Column(name = "name")
private String name;
// PropertyChangeSupport 實現屬性變更通知 (綁定框架需要)
@Transient
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
// Getter/Setter 中火屬性變更事件...
public void setUsernmae(String usernmae) {
String old = this.usernmae;
this.usernmae = usernmae;
changeSupport.firePropertyChange("usernmae", old, usernmae);
}
// ... 其他字段類似
}
核心初始化代碼 (構造函數中):
public MasterDetailForm() {
initComponents(); // GUI 構建器生成的組件初始化
if (!Beans.isDesignTime()) { // 非設計模式才初始化 JPA
// 1. 創建 EntityManagerFactory (線程安全、重量級、應用級單例)
entityManager = Persistence.createEntityManagerFactory("ccPU").createEntityManager();
// 2. 開啟事務 (桌面應用常用手動事務控制)
entityManager.getTransaction().begin();
// 3. 創建查詢 (使用 NamedQuery)
query = entityManager.createNamedQuery("User.findAll");
// 4. 綁定數據源:查詢結果 → ObservableList → JTable
list = ObservableCollections.observableList(query.getResultList());
}
}
private void newButtonActionPerformed(ActionEvent evt) {
User u = new User(); // 1. 創建瞬態實例
entityManager.persist(u); // 2. 持久化 (分配 ID、納入管理)
list.add(u); // 3. 更新 UI 綁定列表
int row = list.size() - 1;
masterTable.setRowSelectionInterval(row, row); // 選中新行
masterTable.scrollRectToVisible(masterTable.getCellRect(row, 0, true));
}
private void deleteButtonActionPerformed(ActionEvent evt) {
int[] selected = masterTable.getSelectedRows();
List toRemove = new ArrayList<>(selected.length);
for (int idx : selected) {
// 轉換視圖行索引 → 模型索引 (關鍵:排序/過濾後索引不一致)
User u = list.get(masterTable.convertRowIndexToModel(idx));
toRemove.add(u);
entityManager.remove(u); // 從持久化上下文移除 (SQL 待提交執行)
}
list.removeAll(toRemove); // 更新 UI
}
private void saveButtonActionPerformed(ActionEvent evt) {
try {
entityManager.getTransaction().commit(); // 提交事務,執行 SQL
entityManager.getTransaction().begin(); // 開啟新事務
} catch (RollbackException rex) {
rex.printStackTrace();
entityManager.getTransaction().begin(); // 重新開啟事務
// 合併策略:重新附托管實體
List merged = new ArrayList<>(list.size());
for (User u : list) {
merged.add(entityManager.merge(u)); // merge 返回新的受管實體
}
list.clear();
list.addAll(merged); // 更新綁定列表為受管實體
}
}
private void refreshButtonActionPerformed(ActionEvent evt) {
entityManager.getTransaction().rollback(); // 回滾未提交變更
entityManager.getTransaction().begin(); // 新事務
Collection data = query.getResultList(); // 重新查詢數據庫
for (Object entity : data) {
entityManager.refresh(entity); // 強制刷新實體狀態
}
list.clear();
list.addAll(data); // 更新 UI
}
| 操作 | 核心 API | 事務行為 | UI 同步策略 |
|---|---|---|---|
| 新增 | persist() | 事務提交時 INSERT | 立即加入綁定列表 |
| 刪除 | remove() | 事務提交時 DELETE | 立即從綁定列表移除 |
| 修改 | 自動髒檢查 | 事務提交時 UPDATE | PropertyChange 事件驅動 |
| 保存 | commit() / merge() | 提交/合併托管態 | 異常時重新綁定 merge 結果 |
| 刷新 | rollback() / refresh() | 回滾 + 重讀 | 清空列表重新加載 |
NetBeans 自帶 EclipseLink,Eclipse 需手動:
META-INF/persistence.xml (參考上文內容)NetBeans 自動生成的 JPA 桌面應用展示了 Java SE 環境下 JPA 完整工作流:EntityManagerFactory 創建 → EntityManager 管理實體生命周期 → 事務手動控制 → JPQL/NamedQuery 查詢 → Swing 綁定框架雙向同步。理解這套機制,可將 JPA 應用於任何 Java SE 場景:桌面客戶端、批處理程序、微服務、測試工具等,擺脫「JPA 僅限 Java EE」的誤區。