Appearance
Spring 编程式事务管理深度解析 🎯
前言:为什么需要编程式事务管理?
在 Spring 的世界里,我们通常使用 @Transactional
注解来处理事务,这种方式简单优雅。但有时候,我们需要更精细的控制——比如在一个方法中需要多个不同配置的事务,或者需要根据运行时条件动态决定事务行为。这时,编程式事务管理就派上用场了!
NOTE
编程式事务管理让你完全掌控事务的生命周期,虽然代码会稍微复杂一些,但换来的是最大的灵活性。
核心概念:Spring 事务管理的两种编程式方式
Spring 提供了两种主要的编程式事务管理方式:
TIP
Spring 团队推荐:对于传统的命令式代码使用 TransactionTemplate
,对于响应式代码使用 TransactionalOperator
。
方式一:使用 TransactionTemplate ⭐
基本原理
TransactionTemplate
采用了模板方法模式,就像 JdbcTemplate
一样。它帮我们处理了事务资源的获取和释放,我们只需要专注于业务逻辑。
核心使用方式
kotlin
@Service
class UserService(
private val userRepository: UserRepository,
transactionManager: PlatformTransactionManager
) {
// 单例 TransactionTemplate,线程安全
private val transactionTemplate = TransactionTemplate(transactionManager)
fun transferMoney(fromUserId: Long, toUserId: Long, amount: BigDecimal): String {
return transactionTemplate.execute { status ->
try {
// 业务逻辑:转账操作
val fromUser = userRepository.findById(fromUserId)
?: throw IllegalArgumentException("转出用户不存在")
val toUser = userRepository.findById(toUserId)
?: throw IllegalArgumentException("转入用户不存在")
if (fromUser.balance < amount) {
throw IllegalStateException("余额不足")
}
// 执行转账
fromUser.balance -= amount
toUser.balance += amount
userRepository.save(fromUser)
userRepository.save(toUser)
"转账成功:${amount}元" // 返回结果
} catch (e: Exception) {
status.setRollbackOnly()
throw e
}
} ?: "转账失败"
}
}
kotlin
fun updateUserBatch(userIds: List<Long>) {
transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
override fun doInTransactionWithoutResult(status: TransactionStatus) {
try {
userIds.forEach { userId ->
val user = userRepository.findById(userId)
user?.let {
it.lastUpdateTime = LocalDateTime.now()
userRepository.save(it)
}
}
} catch (e: Exception) {
status.setRollbackOnly()
throw e
}
}
})
}
自定义事务配置
有时候我们需要为特定的业务场景配置不同的事务参数:
kotlin
@Service
class ReportService(transactionManager: PlatformTransactionManager) {
// 只读事务模板 - 用于复杂查询
private val readOnlyTemplate = TransactionTemplate(transactionManager).apply {
isReadOnly = true
isolationLevel = TransactionDefinition.ISOLATION_READ_COMMITTED
timeout = 60 // 60秒超时
}
// 写事务模板 - 用于数据修改
private val writeTemplate = TransactionTemplate(transactionManager).apply {
isolationLevel = TransactionDefinition.ISOLATION_REPEATABLE_READ
timeout = 30 // 30秒超时
propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED
}
fun generateComplexReport(): ReportData {
return readOnlyTemplate.execute {
// 复杂的只读查询操作
val userData = userRepository.findAllWithDetails()
val orderData = orderRepository.findAllWithItems()
ReportData(userData, orderData)
} ?: throw RuntimeException("报表生成失败")
}
fun updateReportCache(reportData: ReportData) {
writeTemplate.execute(object : TransactionCallbackWithoutResult() {
override fun doInTransactionWithoutResult(status: TransactionStatus) {
reportCacheRepository.deleteAll()
reportCacheRepository.saveAll(reportData.toCacheEntities())
}
})
}
}
IMPORTANT
TransactionTemplate
实例是线程安全的,可以在多个方法间共享。但如果需要不同的事务配置,就需要创建不同的实例。
方式二:使用 TransactionalOperator(响应式) 🚀
对于使用 WebFlux 的响应式应用,我们需要使用 TransactionalOperator
:
kotlin
@Service
class ReactiveUserService(
private val userRepository: ReactiveUserRepository,
transactionManager: ReactiveTransactionManager
) {
private val transactionalOperator = TransactionalOperator.create(transactionManager)
suspend fun transferMoneyReactive(
fromUserId: Long,
toUserId: Long,
amount: BigDecimal
): String {
return transactionalOperator.executeAndAwait {
val fromUser = userRepository.findById(fromUserId).awaitSingle()
val toUser = userRepository.findById(toUserId).awaitSingle()
if (fromUser.balance < amount) {
throw IllegalStateException("余额不足")
}
fromUser.balance -= amount
toUser.balance += amount
userRepository.save(fromUser).awaitSingle()
userRepository.save(toUser).awaitSingle()
"转账成功:${amount}元"
}
}
// 使用 Reactor 操作符风格
fun transferMoneyFlux(fromUserId: Long, toUserId: Long, amount: BigDecimal): Mono<String> {
val transferMono = userRepository.findById(fromUserId)
.zipWith(userRepository.findById(toUserId))
.flatMap { tuple ->
val (fromUser, toUser) = tuple
if (fromUser.balance < amount) {
return@flatMap Mono.error<String>(IllegalStateException("余额不足"))
}
fromUser.balance -= amount
toUser.balance += amount
userRepository.save(fromUser)
.then(userRepository.save(toUser))
.thenReturn("转账成功:${amount}元")
}
return transferMono.`as`(transactionalOperator::transactional)
}
}
WARNING
在响应式编程中,取消信号会导致事务回滚。确保完全消费 Flux
或其他多值 Publisher
的输出,以允许事务完成。
方式三:直接使用 TransactionManager 🔧
当你需要最大程度的控制时,可以直接使用事务管理器:
kotlin
@Service
class AdvancedUserService(
private val txManager: PlatformTransactionManager,
private val userRepository: UserRepository
) {
fun complexBusinessOperation(): String {
val def = DefaultTransactionDefinition().apply {
name = "ComplexBusinessTx"
propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED
isolationLevel = TransactionDefinition.ISOLATION_READ_COMMITTED
timeout = 30
}
val status = txManager.getTransaction(def)
try {
// 第一阶段:数据验证
val users = userRepository.findAllActiveUsers()
if (users.isEmpty()) {
throw IllegalStateException("没有活跃用户")
}
// 第二阶段:业务处理
users.forEach { user ->
user.lastLoginTime = LocalDateTime.now()
userRepository.save(user)
}
// 第三阶段:提交事务
txManager.commit(status)
return "操作成功,处理了 ${users.size} 个用户"
} catch (ex: Exception) {
txManager.rollback(status)
throw ex
}
}
}
kotlin
@Service
class ReactiveAdvancedService(
private val txManager: ReactiveTransactionManager,
private val userRepository: ReactiveUserRepository
) {
fun complexReactiveOperation(): Mono<String> {
val def = DefaultTransactionDefinition().apply {
name = "ComplexReactiveTx"
propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED
}
return txManager.getReactiveTransaction(def)
.flatMap { status ->
val businessLogic = userRepository.findAllActiveUsers()
.collectList()
.flatMap { users ->
if (users.isEmpty()) {
Mono.error(IllegalStateException("没有活跃用户"))
} else {
val updatedUsers = users.map { user ->
user.lastLoginTime = LocalDateTime.now()
user
}
userRepository.saveAll(updatedUsers)
.collectList()
.map { "操作成功,处理了 ${it.size} 个用户" }
}
}
businessLogic
.flatMap { result ->
txManager.commit(status).thenReturn(result)
}
.onErrorResume { ex ->
txManager.rollback(status)
.then(Mono.error(ex))
}
}
}
}
实际应用场景对比 📊
让我们看看在实际业务中,不同方式的适用场景:
kotlin
// 使用 TransactionTemplate 处理批量操作
@Service
class BatchProcessService(
private val transactionTemplate: TransactionTemplate,
private val orderRepository: OrderRepository
) {
fun processBatchOrders(orderIds: List<Long>): BatchResult {
return transactionTemplate.execute { status ->
val successIds = mutableListOf<Long>()
val failedIds = mutableListOf<Long>()
orderIds.forEach { orderId ->
try {
val order = orderRepository.findById(orderId)
order?.let {
it.status = OrderStatus.PROCESSED
it.processedAt = LocalDateTime.now()
orderRepository.save(it)
successIds.add(orderId)
}
} catch (e: Exception) {
failedIds.add(orderId)
// 注意:这里不抛出异常,继续处理其他订单
}
}
if (failedIds.isNotEmpty() && successIds.isEmpty()) {
status.setRollbackOnly()
}
BatchResult(successIds, failedIds)
} ?: BatchResult(emptyList(), orderIds)
}
}
kotlin
// 根据条件决定是否开启事务
@Service
class ConditionalTransactionService(
private val txManager: PlatformTransactionManager,
private val dataRepository: DataRepository
) {
fun processData(data: DataEntity, useTransaction: Boolean): String {
if (!useTransaction) {
// 不使用事务的简单处理
return dataRepository.save(data).let { "数据保存成功" }
}
// 使用事务的复杂处理
val def = DefaultTransactionDefinition().apply {
isolationLevel = when (data.priority) {
Priority.HIGH -> TransactionDefinition.ISOLATION_SERIALIZABLE
Priority.MEDIUM -> TransactionDefinition.ISOLATION_REPEATABLE_READ
else -> TransactionDefinition.ISOLATION_READ_COMMITTED
}
}
val status = txManager.getTransaction(def)
try {
// 复杂的业务逻辑
val validatedData = validateData(data)
val enrichedData = enrichData(validatedData)
val savedData = dataRepository.save(enrichedData)
txManager.commit(status)
return "事务处理成功:${savedData.id}"
} catch (ex: Exception) {
txManager.rollback(status)
throw ex
}
}
}
最佳实践与注意事项 💡
1. 选择合适的方式
2. 异常处理策略
CAUTION
编程式事务管理中,你需要手动处理异常和回滚。不像声明式事务会自动回滚运行时异常。
kotlin
// ❌ 错误的异常处理
transactionTemplate.execute { status ->
try {
businessLogic()
} catch (e: Exception) {
// 忘记调用 setRollbackOnly()
return@execute "处理失败"
}
}
// ✅ 正确的异常处理
transactionTemplate.execute { status ->
try {
businessLogic()
"处理成功"
} catch (e: Exception) {
status.setRollbackOnly()
throw e // 重新抛出异常
}
}
3. 性能优化建议
性能优化要点
- 复用 TransactionTemplate 实例:它们是线程安全的
- 合理设置超时时间:避免长时间锁定资源
- 选择合适的隔离级别:平衡一致性和性能
- 批量操作优化:减少事务开销
总结:编程式 vs 声明式事务管理 🎯
特性 | 编程式事务管理 | 声明式事务管理 |
---|---|---|
灵活性 | ⭐⭐⭐⭐⭐ 完全控制 | ⭐⭐⭐ 配置驱动 |
简洁性 | ⭐⭐ 代码较多 | ⭐⭐⭐⭐⭐ 注解即可 |
性能 | ⭐⭐⭐⭐ 精确控制 | ⭐⭐⭐⭐ AOP 开销 |
维护性 | ⭐⭐⭐ 需要手动管理 | ⭐⭐⭐⭐⭐ 框架管理 |
适用场景 | 复杂业务逻辑 | 常规 CRUD 操作 |
最终建议
- 日常开发:优先使用
@Transactional
声明式事务 - 复杂场景:使用
TransactionTemplate
或TransactionalOperator
- 极致控制:直接使用
TransactionManager
编程式事务管理虽然代码量更多,但它给了你完全的控制权。在需要精细化事务管理的场景下,它是不可替代的工具! 🚀