純 Hibernate 時代,每次操作需手動開啟/提交/回滾事務,代碼分散、易漏提交、異常處理繁瑣。Spring 提供聲明式事務管理,通過 AOP 切面攔截 Service 方法,自動處理事務邊界,實現業務邏輯與事務邏輯解耦。
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
Hibernate 5 使用 HibernateTransactionManager,Hibernate 4 同包路徑。事務管理器負責從 SessionFactory 獲取/綁定 Session 到線程局部變量,實現「一請求一 Session」。
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 增刪改方法:必須在事務中運行 -->
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<!-- 查詢方法:只讀事務,優化資料庫鎖 -->
<tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="query*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="list*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="count*" propagation="NOT_SUPPORTED" read-only="true"/>
<!-- 其他方法默認只讀 -->
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethods"
expression="execution(* com.example.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>
關鍵原則:事務邊界劃在 Service 層,而非 DAO 層。DAO 粒度太細,無法保證跨多個 DAO 操作的原子性;Service 組合業務邏輯,天然符合事務邊界。
| 常量 | 行為描述 | 典型場景 |
|---|---|---|
| REQUIRED (默認) | 有事務則加入,無則新建。最常用,保證同一邏輯事務。 | 增刪改業務方法 |
| SUPPORTS | 有事務則加入,無則以非事務運行。 | 查詢方法,允許在事務外獨立調用 |
| MANDATORY | 必須在現有事務中運行,否則拋異常。 | 強制要求事務上下文的核心操作 |
| REQUIRES_NEW | 總是新建事務,掛起現有事務。獨立提交/回滾。 | 日誌記錄、審計、獨立計費扣款 |
| NOT_SUPPORTED | 以非事務方式運行,掛起現有事務。 | 大批量只讀查詢、長時間報表導出 |
| NEVER | 非事務運行,若存在事務則拋異常。 | 嚴格禁止事務的外部接口調用 |
| NESTED | 嵌套事務 (Savepoint),外層回滾內層必回滾,內層回滾外層可選擇提交。 | JDBC 支持 Savepoint 的數據庫,細粒度回滾 |
<tx:method name="transfer" propagation="REQUIRED"
rollback-for="java.lang.Exception"
no-rollback-for="com.example.BusinessWarningException"/>
Exception 覆蓋所有受檢異常。<tx:annotation-driven transaction-manager="transactionManager" />
@Service
@Transactional(readOnly = true) // 類級別默認只讀
public class AccountServiceImpl implements AccountService {
@Override
@Transactional(readOnly = false, rollbackFor = Exception.class)
public void transfer(Long from, Long to, BigDecimal amount) {
// 業務邏輯...
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public AccountDTO getAccount(Long id) { ... }
}
事務配置在 Service 層對二級緩存 (Ehcache/Redis) 有益:
afterCompletion 觸發二級緩存更新,保證緩存與數據庫一致性。readOnly=true 且連接池支持 (HikariCP 自動優化)。HibernateTransactionManager 不支持,需引入 JtaTransactionManager (Atomikos/Bitronix) 實現 XA 事務。Spring 聲明式事務通過 TransactionManager + tx:advice + AOP 或 @Transactional 實現業務與事務分離。核心決策:Service 層開啟事務、增刷改用 REQUIRED+rollbackFor=Exception、查詢用 NOT_SUPPORTED+readOnly、特殊獨立業務用 REQUIRES_NEW。配合合理的異常體系設計,構建健壯的數據一致性保障體系。