前文介紹的容器管理 EntityManager(Container-Managed, CMT)由 EJB 容器負責生命週期,僅適用於 Session Bean 等受管元件。若在 Servlet、JSF Managed Bean 或獨立 Java SE 環境中需直接操作 JPA,必須改用應用程式管理 EntityManager(Application-Managed, AMT)。本文詳解配置差異與程式碼實作。
| 特性 | 容器管理 | 應用程式管理 |
|---|---|---|
| 建立方式 | @PersistenceContext 注入 | EntityManagerFactory.createEntityManager() |
| 交易類型 | JTA (Java EE 容器交易) | RESOURCE_LOCAL (本地交易) |
| 關閉責任 | 容器自動管理 | 開發者手動 em.close()、emf.close() |
| 適用場景 | EJB、CDI Bean、JSF Managed Bean | Servlet、Filter、獨立應用、測試程式 |
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://java.sun.com/xml/ns/persistence"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://java.sun.com/xml/ns/persistence
https://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="myentity" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>java:/MyPIM</non-jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="none" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
transaction-type="RESOURCE_LOCAL":聲明使用本地交易,而非 JTA<non-jta-data-source>:替換原 <jta-data-source>,資料源名稱不變提示:若同專案仍需容器管理 EM,可在同檔案新增另一個 <persistence-unit> 並指定不同 name。
public class MyServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 建立 EntityManagerFactory(昂貴操作,實際專案應做成單例或注入)
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myentity");
// 2. 建立 EntityManager
EntityManager em = emf.createEntityManager();
try {
// 3. 業務操作:查詢 ID=1 的 DateBean
DateBean db = em.find(DateBean.class, 1);
response.getWriter().println(db.getMyDate());
} finally {
// 4. 必須關閉 EM 與 EMF 釋放資源
em.close();
emf.close();
}
}
}
AMT 模式下需手動管理交易:
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
// persist / merge / remove 操作
tx.commit();
} catch (Exception e) {
if (tx.isActive()) tx.rollback();
throw e;
} finally {
em.close();
}
@WebListener
public class EMFListener implements ServletContextListener {
private static EntityManagerFactory emf;
@Override
public void contextInitialized(ServletContextEvent sce) {
emf = Persistence.createEntityManagerFactory("myentity");
sce.getServletContext().setAttribute("emf", emf);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
if (emf != null && emf.isOpen()) emf.close();
}
public static EntityManagerFactory getEMF() { return emf; }
}
Servlet 中取得:EntityManager em = EMFListener.getEMF().createEntityManager();
掌握 AMT 模式,讓 JPA 能脫離 EJB 容器在更廣泛的 Java 環境中運作。記住三原則:EMF 單例、EM 請求級、交易手動控制、資源必釋放。