跳到主要内容

事务性变更操作

在本节中,我们将延续 GraphQL 示例,讲解如何将 GraphQL 变更操作设置为事务性。也就是说,自动用数据库事务包装 GraphQL 变更操作,在操作成功时提交事务,或在出现 GraphQL 错误时回滚事务。

克隆代码(可选)

本教程的代码可在 github.com/a8m/ent-graphql-example 获取,每个步骤都有对应的 Git 标签。如果您想跳过基础设置,直接从 GraphQL 服务器的初始版本开始,可以按如下方式克隆仓库并检出 v0.1.0

git clone git@github.com:a8m/ent-graphql-example.git
cd ent-graphql-example
go run ./cmd/todo/

使用方法

GraphQL 扩展提供了一个名为 entgql.Transactioner 的处理器,该处理器会在事务中执行每个 GraphQL 变更操作。注入到解析器中的客户端是一个事务性 ent.Client。因此,使用 ent.Client 的 GraphQL 解析器无需更改。要将此功能添加到我们的待办事项列表应用中,请按照以下步骤操作:

1. 编辑 cmd/todo/main.go,在 GraphQL 服务器初始化中添加 entgql.Transactioner 处理器,如下所示:

cmd/todo/main.go
srv := handler.NewDefaultServer(todo.NewSchema(client))
+srv.Use(entgql.Transactioner{TxOpener: client})

2. 然后,在 GraphQL 变更操作中,按如下方式使用上下文中的客户端:

todo.resolvers.go
}
+func (mutationResolver) CreateTodo(ctx context.Context, input ent.CreateTodoInput) (*ent.Todo, error) {
+ client := ent.FromContext(ctx)
+ return client.Todo.Create().SetInput(input).Save(ctx)
-func (r *mutationResolver) CreateTodo(ctx context.Context, input ent.CreateTodoInput) (*ent.Todo, error) {
- return r.client.Todo.Create().SetInput(input).Save(ctx)
}

隔离级别

如果您想要调整事务的隔离级别,可以通过实现自己的 TxOpener 来实现。例如:

cmd/todo/main.go
srv.Use(entgql.Transactioner{
TxOpener: entgql.TxOpenerFunc(func(ctx context.Context) (context.Context, driver.Tx, error) {
tx, err := client.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
if err != nil {
return nil, nil, err
}
ctx = ent.NewTxContext(ctx, tx)
ctx = ent.NewContext(ctx, tx.Client())
return ctx, tx, nil
}),
})

跳过操作

默认情况下,entgql.Transactioner 会将所有变更操作包装在事务中。但是,有些变更操作或操作不需要数据库访问或需要特殊处理。在这些情况下,您可以通过设置自定义的 SkipTxFunc 函数或使用内置函数来指示 entgql.Transactioner 跳过事务。

cmd/todo/main.go
srv.Use(entgql.Transactioner{
TxOpener: client,
// 跳过指定操作名称的事务执行。
SkipTxFunc: entgql.SkipOperations("operation1", "operation2"),
})

srv.Use(entgql.Transactioner{
TxOpener: client,
// 如果操作包含指定名称的变更字段则跳过。
SkipTxFunc: entgql.SkipIfHasFields("field1", "field2"),
})

srv.Use(entgql.Transactioner{
TxOpener: client,
// 自定义跳过函数。
SkipTxFunc: func(*ast.OperationDefinition) bool {
// ...
},
})

太好了!仅用几行代码,我们的应用现在就支持自动的事务性变更操作。请继续阅读下一节,我们将讲解如何扩展 Ent 代码生成器并为 GraphQL 变更操作生成 GraphQL 输入类型