Spring 透過 AOP 實現宣告式事務,核心是 TransactionProxyFactoryBean(舊版)或 <tx:advice>(新版)。事務邊界的位置直接影響資料一致性與效能,常見兩種策略:事務在 DAO 層與事務在 Service 層。
適用於「單一 DAO 操作即為一個事務單元」的簡單場景。
<!-- 交易管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- DAO 目標物件 -->
<bean id="companyDAOTarget"
class="com.vstsoft.querycompany.dao.impl.CompanyDAOImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- DAO 事務代理 -->
<bean id="companyDAOProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target" ref="companyDAOTarget"/>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<!-- Service 注入 DAO 代理 -->
<bean name="companyManageTarget"
class="com.vstsoft.querycompany.service.impl.CompanyManageTarget">
<property name="companyDAO" ref="companyDAOProxy"/>
</bean>
特點:每個 DAO 方法獨立開啟/提交事務;Service 呼叫多個 DAO 方法時,無法保證跨方法的原子性。
符合「一個業務用例=一個事務」的領域驅動設計原則。
<!-- JTA 交易管理器(跨資源) -->
<bean id="jtaTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"/>
<!-- DAO 僅需普通代理(無事務屬性) -->
<bean id="companyDAOProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"
value="com.vstsoft.querycompany.dao.CompanyDAO"/>
<property name="target" ref="companyDAOTarget"/>
</bean>
<!-- Service 事務代理 -->
<bean id="companyManageProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="jtaTransactionManager"/>
<property name="target" ref="companyManageTarget"/>
<property name="transactionAttributes">
<props>
<prop key="set*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
關鍵差異:
1. DAO 不再有事務屬性,僅作為資料存取介面。
2. Service 方法(如 setData())包含「查詢→刪除→插入」多步驟,若任一步驟拋出 RuntimeException,整個事務回滾;全部成功才提交。
3. 若需跨多個資料源,必須使用 JtaTransactionManager。
| 傳播屬性 | 行為 |
|---|---|
| PROPAGATION_REQUIRED | 有事務則加入,無則建立新事務(預設) |
| PROPAGATION_REQUIRES_NEW | 總是建立新事務,掛起現有事務 |
| PROPAGATION_NESTED | 巢狀事務,外層回滾則內層回滾,內層回滾不影響外層 |
| readOnly | 標記唯讀,資料庫可優化鎖策略 |
readOnly 提升效能。@Transactional 註解 + <tx:annotation-driven>,配置更簡潔。事務層級的選擇直接關係資料一致性。理解「DAO 層事務=細粒度、Service 層事務=業務粒度」是架構設計的關鍵決策點。