事务 @Transactional
ACE ORM 的事务管理遵循「声明优先、零模板」原则:在方法上标注 @Transactional,框架在编译期生成 begin / commit / rollback 的包裹代码,运行时无反射开销。
基础用法
标注 @Transactional
package demo.service
import ace.framework.*
import ace.orm.*
@Service
public class OrderService {
@Inject
private let repo: OrderRepository
@Transactional
public func placeOrder(userId: Int64, items: Array<Item>): Order {
let order = repo.save(Order(userId: userId, status: "PENDING"))
for item in items {
repo.saveItem(OrderItem(orderId: order.id, itemId: item.id))
}
// 方法正常返回 → 自动 commit
return order
}
}方法内任意位置抛出 Exception,框架自动执行 ROLLBACK,异常继续向上传播。
传播行为
ACE 支持两种传播行为,通过不同注解选择。
| 注解 | 传播行为 | 说明 |
|---|---|---|
@Transactional | REQUIRED(默认) | 复用调用栈上已有的事务;若无则新建 |
@RequiresNew | REQUIRES_NEW | 始终新建独立事务(共享连接时建 savepoint) |
REQUIRED — 加入已有事务
@Transactional
public func outer(): Unit {
inner() // inner 加入 outer 的事务,共享同一连接
}
@Transactional
public func inner(): Unit {
repo.doSomething()
}outer 提交或回滚时,inner 的操作一并处理。
@RequiresNew — 独立事务 / Savepoint
@Transactional
public func outer(): Unit {
repo.stepA()
auditService.log("step-a") // @RequiresNew,独立提交
repo.stepB()
}
@Service
public class AuditService {
@RequiresNew
public func log(event: String): Unit {
auditRepo.insert(AuditLog(event: event))
// 即使 outer 最终回滚,本次 log 已独立提交
}
}Savepoint 机制
当外层事务持有的连接被内层 @RequiresNew 复用时,框架自动在该连接上执行 SAVEPOINT sp_N。内层回滚仅回退到 savepoint,不影响外层事务。
嵌套调用
多层 @Transactional 调用在同一协程内共享同一事务上下文(即同一数据库连接)。
@Transactional
public func createUserWithProfile(req: CreateUserReq): User {
let user = userRepo.save(User(name: req.name))
profileService.create(user.id, req.profile) // 同事务
return user
}
// ProfileService
@Transactional
public func create(userId: Int64, profile: Profile): Unit {
profileRepo.save(ProfileRecord(userId: userId, ...profile))
}外层事务回滚时,内层操作也一并回滚。
perOp 模式说明
SQLite 默认采用 perOp 模式(每次数据库操作独立获取连接),此模式下:
@Transactional仍然有效(同一方法内的操作在同一连接上执行事务)。@RequiresNew的 savepoint 无回滚隔离效果,因为内外层实际使用不同连接。
SQLite perOp 限制
使用 SQLite 时,若需要 @RequiresNew 的独立回滚语义,请将连接池配置为 poolMode = "shared" 或切换到支持连接复用的数据库(如 PostgreSQL)。
# cjpm.toml 或 ace.toml
[database]
url = "sqlite://./data.db"
poolMode = "shared" # 启用连接复用,savepoint 才生效手动事务
不方便使用注解时(例如动态构建多步操作),可使用 withTransaction Lambda 形式:
import ace.orm.withTransaction
public func batchImport(rows: Array<Row>): Unit {
withTransaction {
for row in rows {
repo.insert(row)
}
// Lambda 正常返回 → commit
// Lambda 内抛出异常 → rollback
}
}withTransaction 的返回值与 Lambda 返回值类型一致:
let total: Int64 = withTransaction {
repo.sumAll()
}回滚条件
| 场景 | 行为 |
|---|---|
| 方法正常返回 | 自动 COMMIT |
方法抛出任意 Exception | 自动 ROLLBACK |
withTransaction Lambda 正常退出 | 自动 COMMIT |
withTransaction Lambda 抛出异常 | 自动 ROLLBACK,异常重新抛出 |
@Async 与事务
跨协程事务不共享
@Async 方法在独立协程中运行,无法继承调用方的事务上下文。在 @Async 方法内调用带 @Transactional 的操作时,会开启一个全新事务,与外层事务完全隔离。
@Transactional
public func outer(): Unit {
repo.stepA()
asyncService.doSomething() // 在新协程中,独立事务!
repo.stepB()
// stepA 和 stepB 在同一事务;doSomething 在独立事务
}
@Service
public class AsyncService {
@Async
@Transactional
public func doSomething(): Unit {
repo.asyncStep()
}
}如需跨异步边界协调事务,请在 @Async 方法的返回值 Future 上使用 await 后再处理提交逻辑,或将异步调用移至事务边界之外。
API 速查
| 注解 / 函数 | 位置 | 说明 |
|---|---|---|
@Transactional | 方法 | REQUIRED 传播,自动 begin/commit/rollback |
@RequiresNew | 方法 | REQUIRES_NEW 传播,建立独立事务或 savepoint |
withTransaction { } | 调用处 | 手动划定事务边界,Lambda 作为事务体 |