// DAO 層硬編碼,違反 IOC 原則
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myJpa");
EntityManager em = emf.createEntityManager();
優勢:生命週期托管、事務整合、異常轉換、線程綁定、延遲加載支援
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="https://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://xmlns.jcp.org/xml/ns/persistence
https://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>entity.Product</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
<property name="hibernate.connection.driver_class" value="com.mysql.cj.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/product_db?useSSL=false&serverTimezone=UTC"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="123456"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="https://www.springframework.org/schema/tx"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation="
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1. 掃描組件 -->
<context:component-scan base-package="com.demo" />
<context:annotation-config />
<!-- 2. EntityManagerFactory Bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="myJpa" />
</bean>
<!-- 3. 事務管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 4. 註解驅動事務 -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
@Repository
public class ProductDaoImpl implements ProductDao {
// 方式 1:JPA 標準註解 (推薦),自動綁定當前事務的 EntityManager
@PersistenceContext
private EntityManager entityManager;
// 方式 2:Spring 自定義註解 (需 Qualifier 指定 Bean 名)
// @Autowired
// @Qualifier("entityManagerFactory")
// private EntityManagerFactory entityManagerFactory; // 需手動 createEntityManager()
@Override
@Transactional // 方法級事務,或類級
public Product save(Product product) {
entityManager.persist(product);
return product;
}
@Override
@Transactional(readOnly = true)
public Product findById(Long id) {
return entityManager.find(Product.class, id);
}
@Override
@Transactional
public void deleteById(Long id) {
Product product = entityManager.find(Product.class, id);
if (product != null) {
entityManager.remove(product);
}
}
@Override
@Transactional(readOnly = true)
public List findAll() {
// JPQL 查詢
TypedQuery query = entityManager.createQuery("SELECT p FROM Product p", Product.class);
return query.getResultList();
}
}
| 配置層面 | 關鍵 Bean/註解 | 作用 |
|---|---|---|
| 事務管理器 | JpaTransactionManager | 平台事務管理器實現,綁定 EMF |
| 註解驅動 | <tx:annotation-driven> / @EnableTransactionManagement | 啟用 @Transactional 解析、代理織入 |
| 事務邊界 | @Transactional | 類/方法級宣告事務屬性 |
| 傳播行為 | propagation = REQUIRED (預設) | 支援巢狀事務邏輯 |
| 隔離級別 | isolation = READ_COMMITTED | 資料庫鎖策略 |
| 唯讀優化 | readOnly = true | Hibernate 唯讀優化、資料庫鎖優化 |
| 回滾規則 | rollbackFor = Exception.class | 受檢異常預設不回滾,需顯式指定 |
| 超時 | timeout = 30 | 防止長事務佔用資源 |
| 問題 | 原因 | 解決 |
|---|---|---|
| No EntityManager bound | 事務未生效/未啟用註解驅動 | 檢查 @EnableTransactionManagement、tx:annotation-driven、@Transactional 位置 |
| LazyInitializationException | 事務外訪問懶加載關聯 | OSIV Filter、fetch join、DTO 投影、延長事務邊界 |
| 事務不回滾 | 受檢異常/try-catch 吞異常 | rollbackFor 指定、不捕獲或重新拋出 |
| 多線程 EM 衝突 | 手動 createEntityManager() 共享 | 務必用 @PersistenceContext 注入代理 |
總結:Spring 整合 JPA 核心在於
LocalContainerEntityManagerFactoryBean+JpaTransactionManager+@PersistenceContext+@Transactional四件套。掌握 EntityManager 代理機制、事務傳播規則、懶加載邊界,即可構建健壯的 JPA 數據訪問層。生產環境建議使用 DataSource 方式替換 persistence.xml,接入 HikariCP 連接池。