電商下單扣款:需同時更新「賬戶餘額表」扣減金額、更新「訂單表」狀態為已支付。兩條 SQL 必須在同一事務中原子執行,任一失敗全盤回滾。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
@Service
public class OrderService {
@Autowired JdbcTemplate jdbcTemplate;
@Transactional(rollbackFor = Exception.class)
public void placeOrder(String userId, String orderId, BigDecimal amount) {
// 1. 凍結/扣減賬戶餘額
int updated = jdbcTemplate.update(
"UPDATE account SET balance = balance - ? WHERE user_id = ? AND balance >= ?",
amount, userId, amount
);
if (updated == 0) throw new InsufficientBalanceException("餘額不足");
// 2. 更新訂單狀態
jdbcTemplate.update(
"UPDATE orders SET status = 'PAID', pay_time = NOW() WHERE id = ? AND status = 'PENDING'",
orderId
);
// 3. 記錄流水 (可選)
jdbcTemplate.update(
"INSERT INTO transaction_log (order_id, amount, type) VALUES (?, ?, 'PAY')",
orderId, amount
);
}
}
@Transactional
public void batchInsertUsers(List<User> users) {
String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)";
List<Object[]> batchArgs = users.stream()
.map(u -> new Object[]{u.getId(), u.getName(), u.getEmail()})
.collect(Collectors.toList());
int[] result = jdbcTemplate.batchUpdate(sql, batchArgs);
// result[i] 為每條語句影響行數
}
@Transactional
public void executeMultipleStatements() {
String[] sqls = new String[]{
"DELETE FROM role_menu WHERE role_id = 'R001'",
"INSERT INTO role_menu (role_id, menu_id) VALUES ('R001', 'M001')",
"INSERT INTO role_menu (role_id, menu_id) VALUES ('R001', 'M002')",
"UPDATE role SET last_modified = NOW() WHERE id = 'R001'"
};
int[] results = jdbcTemplate.batchUpdate(sqls);
// 任一拋出異常 → 事務回滾,全部不生效
}
// ❌ 危險示例:無 @Transactional
public void dangerousExample() {
jdbcTemplate.update("DELETE FROM role_menu WHERE role_id = 'R001'");
// 若此處拋異常 (如網絡中斷),DELETE 已提交,INSERT 不會執行
// 導致數據不一致:關係被刪除但新關係未建立
jdbcTemplate.update("INSERT INTO role_menu ...");
}
batchUpdate(String[]) 內部創建 Statement (非 PreparedStatement)stmt.addBatch(sql) 將所有 SQL 加入批處理stmt.executeBatch() 一次性發送到數據庫執行connection.setAutoCommit(false)connection.commit();拋異常 → connection.rollback()rollbackFor = Exception.classbatchUpdate(String, List<Object[]>) 參數化形式,而非拼接 SQL 字符串@Transactional(readOnly = true)@Autowired PlatformTransactionManager txManager;
public void complexBusinessLogic() {
TransactionTemplate template = new TransactionTemplate(txManager);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
template.execute(status -> {
try {
jdbcTemplate.batchUpdate(sqlArray);
return true;
} catch (Exception e) {
status.setRollbackOnly();
throw new BusinessException("批量執行失敗", e);
}
});
}
Spring JdbcTemplate batchUpdate(String[]) 配合 @Transactional 是實現多條 SQL 原子執行的標準方案。關鍵在於:聲明式事務邊界清晰、異常回滾機制可靠、批量執行減少網絡往返。生產環境務必驗證事務隔離級別、鎖等待超時配置,並建立事務監控告警體系。