事務四大特性 (ACID)
| 特性 | 說明 |
| 原子性 | 事務內操作要麼全成功,要麼全失敗回滾 |
| 一致性 | 資料庫從一個一致狀態轉移到另一個一致狀態 |
| 隔離性 | 並發事務間互不干擾,通過隔離級別控制 |
| 持久性 | 提交後的修改永久生效,不因系統故障丟失 |
Spring 事務管理兩大範式
- 編程式事務:代碼中顯式控制提交/回滾,靈活但侵入性強,維護成本高。
- 聲明式事務:基於 AOP,通過註解或 XML 配置切面,業務代碼零侵入,推薦生產環境使用。
本地事務 vs 全局事務
- 本地事務:單一資源(如單 DataSource),輕量,Spring
DataSourceTransactionManager 管理。
- 全局事務:跨多資源(多庫、JMS 等),需 JTA 管理器(
JtaTransactionManager)與應用服務器協同。
編程式事務完整實現
public class StudentJDBCTemplate implements StudentDAO {
private PlatformTransactionManager txManager;
private JdbcTemplate jdbcTemplate;
public void setTransactionManager(PlatformTransactionManager txManager) {
this.txManager = txManager;
}
@Override
public void create(String name, Integer age, Integer marks, Integer year) {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = txManager.getTransaction(def);
try {
jdbcTemplate.update("INSERT INTO Student(name,age) VALUES(?,?)", name, age);
Integer sid = jdbcTemplate.queryForObject("SELECT MAX(id) FROM Student", Integer.class);
jdbcTemplate.update("INSERT INTO Marks(sid,marks,year) VALUES(?,?,?)", sid, marks, year);
txManager.commit(status);
} catch (DataAccessException e) {
txManager.rollback(status);
throw e;
}
}
// listStudents 省略
}
<!-- XML 配置事務管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
聲明式事務:註解驅動(現代首選)
<!-- 啟用註解事務 -->
<tx:annotation-driven transaction-manager="txManager"/>
@Service
public class StudentServiceImpl implements StudentService {
@Autowired StudentDAO studentDAO;
@Transactional(rollbackFor = Exception.class) // 預設僅回滾 RuntimeException
public void createStudent(String name, Integer age, Integer marks, Integer year) {
studentDAO.create(name, age, marks, year);
// 模擬業務異常觸發回滾
if ("error".equals(name)) throw new RuntimeException("模擬異常");
}
}
聲明式事務:XML 配置式(傳統專案維護)
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="create*" rollback-for="java.lang.Exception"/>
<tx:method name="*"/> <!-- 其他方法預設只讀 -->
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceOps" expression="execution(* com.example.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOps"/>
</aop:config>
事務屬性核心配置
| 屬性 | 說明 | 常用值 |
| propagation | 傳播行為 | REQUIRED(預設)、REQUIRES_NEW、NESTED |
| isolation | 隔離級別 | READ_COMMITTED(預設)、REPEATABLE_READ、SERIALIZABLE |
| timeout | 超時秒數 | -1 表示無超時 |
| readOnly | 只讀優化 | true/false |
| rollbackFor | 觸發回滾的異常 | Exception.class(建議顯式指定) |
常見坑點與避雷指南
- 內部方法調用失效:同類內
this.method() 繞過代理,需注入自身或拆分類。
- 異常被吞噬:
try-catch 未重拋異常導致事務不回滾。
- 非 public 方法:
@Transactional 僅對 public 方法生效(protected/private 需 AspectJ)。
- 資料源未配置事務管理器:多 DataSource 時需指定
@Transactional("txManagerName")。
總結建議
- 優先使用 @Transactional 註解,配合
rollbackFor = Exception.class。
- 合理設計傳播行為,避免巢狀事務不可控。
- 讀多寫少場景開啟
readOnly=true 讓數據庫優化鎖策略。
- 長事務拆分為短事務,減少鎖持有時間,提升並發吞吐。