# 事务

## 基本机制 <a href="#ji-ben-ji-zhi" id="ji-ben-ji-zhi"></a>

创建数据库连接之后，我们使用该数据库连接创建事务管理器。

```go
db := rdb.Connect("local.properties")
tm := rdb.NewTransactionManager(db)
```

GoooQo中的事务管理通过[TransactionManager](https://github.com/doytowin/goooqo/blob/main/core/core.go#L60)和[TransactionContext](https://github.com/doytowin/goooqo/blob/main/core/core.go#L66)（以下简称TC）配合完成。

```go
package core

import (
	"context"
	"database/sql/driver"
)

type TransactionManager interface {
	GetClient() any
	StartTransaction(ctx context.Context) (TransactionContext, error)
	SubmitTransaction(ctx context.Context, callback func(tc TransactionContext) error) error
}

type TransactionContext interface {
	context.Context
	driver.Tx
	Parent() context.Context
	SavePoint(name string) error
	RollbackTo(name string) error
}
```

`TransactionManager`中的方法`StartTransaction`负责开启事务并返回TC；TC组合了`driver.Tx`，负责事务的提交和回滚。

[TxDataAccess](https://github.com/doytowin/goooqo/blob/main/core/core.go#L74)接口组合了[DataAccess](https://github.com/doytowin/goooqo/blob/main/core/core.go#L46)和`TransactionManager`，可以在实现数据库访问接口的同时，更方便的进行事务管理。

```go
type TxDataAccess[E Entity] interface {
	TransactionManager
	DataAccess[E]
}
```

## 事务使用示例 <a href="#shi-wu-shi-yong-shi-li" id="shi-wu-shi-yong-shi-li"></a>

使用`TransactionManager#StartTransaction`开启事务，手动提交或者回滚事务：

```go
tc, err := userDataAccess.StartTransaction(ctx)
userQuery := UserQuery{ScoreLt: P(80)}
cnt, err := userDataAccess.DeleteByQuery(tc, userQuery)
if err != nil {
	err = RollbackFor(tc, err)
	return 0
}
err = tc.Commit()
return cnt
```

或者使用`TransactionManager#SubmitTransaction`通过回调的方式提交事务：

```go
err := tm.SubmitTransaction(ctx, func(tc TransactionContext) (err error) {
    // transaction body
    return
})
```

## 事务的传播管理 <a href="#shi-wu-de-chuan-bo-guan-li" id="shi-wu-de-chuan-bo-guan-li"></a>

在Spring的事务传播机制中定义了以下7个级别，`GoooQo`中的对应处理方式如下：

* REQUIRED

  使用任意context调用`TxDataAccess`的`StartTransaction`：

  如果context为TC，则将context强制转化为TC后返回；

  如果context不是TC，则调用`db#BeginTx`开启事务获取`sql.Tx`，再通过context和`sql.Tx`创建TC后返回。
* SUPPORTS

  使用任意`Context`调用`TxDataAccess`的数据库访问方法。
* REQUIRES\_NEW

  当`ctx`为TC时，使用`ctx.(TC).Context`开启事务；\
  当`ctx`不为TC时，使用`ctx`开启事务；
* NOT\_SUPPORTED

  当`ctx`为TC时，使用`ctx.(TC).Context`调用`TxDataAccess`的数据库访问方法；\
  当`ctx`不为TC时，使用`ctx`调用`TxDataAccess`的数据库访问方法；
* MANDATORY:

  对传入的`ctx`不是TC的情况进行处理。
* NEVER

  对传入的`ctx`是TC的情况进行处理。
* NESTED

  使用TC的`SavePoint/RollbackTo`方法。

这里整理了一个表格对前4个传播级别进行了对比：

| context参数\数据库操作         | 开启事务          | 调用数据库          |
| ----------------------- | ------------- | -------------- |
| 任意Context               | REQUIRED      | SUPPORTS       |
| ctx.(TC).Context \| ctx | REQUIRES\_NEW | NOT\_SUPPORTED |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://goooqo.docs.doyto.win/zh/api/transaction.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
