105 lines
6.2 KiB
Markdown
105 lines
6.2 KiB
Markdown
# 可选 GORM 适配器
|
||
|
||
从 `v0.14.1-preview` 提供。`gormx` 把 GORM 接到 go-omni 已初始化的 `*sql.DB`,保留一份连接池和一处资源所有权。使用 GORM v2 API(本版验收版本为 `gorm.io/gorm v1.31.2`、MySQL dialect `v1.6.0`)。
|
||
|
||
核心 `omni`、`database`、`database/mysql` 不导入此包;普通项目的编译依赖链不包含 GORM。仓库同一 Go module 的 go.mod 会记录可选适配器依赖,使用方显式导入 `database/gormx` 才编译它。本版没有新增默认 ORM 或 `--orm` 脚手架开关。
|
||
|
||
## 生成项目接入
|
||
|
||
```sh
|
||
go install git.yuntuo.hk/k2safe/go-omni/cmd/go-omni@v0.14.1-preview
|
||
go-omni new --database mysql --module example.com/billing ./billing
|
||
```
|
||
|
||
按 [MySQL 配置说明](../mysql/README.md) 设置连接信息、TLS 和池大小。在开发者维护的 `internal/svc/serviceContext.go` 中增加 `ORM *gorm.DB` 字段,并填写现有 Init:
|
||
|
||
```go
|
||
import (
|
||
"context"
|
||
"git.yuntuo.hk/k2safe/go-omni/database/gormx"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// 向现有 ServiceContext 添加 ORM *gorm.DB 字段。
|
||
// 保留 Runtime/Config/Logger/DB 等现有字段。
|
||
func (c *ServiceContext) Init(ctx context.Context) error {
|
||
orm, err := gormx.MySQL(ctx, c.DB, gormx.WithLogger(c.Logger))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
c.ORM = orm
|
||
return nil
|
||
}
|
||
```
|
||
|
||
执行 `go mod tidy`。main、runtimeGen、Handler 和路由无需修改;重新生成保留 svc 的字段与 Init。已有项目先 `go get git.yuntuo.hk/k2safe/go-omni@v0.14.1-preview`,同样接入;v0.14.0 生成项目的目录和生命周期契约不变。
|
||
|
||
Repository 接收 `*gorm.DB`,查询使用请求 context:
|
||
|
||
```go
|
||
err := repo.db.WithContext(ctx).Where("id = ?", id).First(&item).Error
|
||
|
||
err = repo.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||
// 同一事务内始终使用 tx。
|
||
return tx.Create(&item).Error
|
||
})
|
||
```
|
||
|
||
现有模型、Hooks、Scopes、事务、命名策略和错误处理继续由项目维护。共享连接池不会把分别发起的 SQL/GORM 事务自动合并;不要在一个事务内切回根 ORM 或 c.DB 执行本应属于该事务的写入。
|
||
|
||
## 连接池与失败处理
|
||
|
||
- MySQL 配置、TLS、字符集、会话参数、池大小及健康检查由底层 `database/mysql` 管理,适配器不再设置另一套 DSN 或池参数。
|
||
- GORM 方言的版本协商继续执行,自动 Ping 关闭以复用已完成的池初始化。初始化查询使用传入 context,并受池的 PingTimeout 限制;可用 `WithInitTimeout(3*time.Second)` 单独指定。
|
||
- 返回成功后不保留启动 context,每次查询用 `WithContext(ctx)`。查询取消、错误、事务提交/回滚保持 GORM 原生语义。
|
||
- 对方言初始化失败时 GORM 自动关闭连接池的路径提供保护;错误仍保留 errors.Is/As,原池继续归调用方管理。生成项目的 Init 返回错误时,再由 Runtime 统一回滚该项目资源。
|
||
- 服务排空后由 go-omni 关闭原池;`orm.DB()` 返回的仍是那一个池。不要给它重复登记 Close,也不要让 Repository 关闭它。
|
||
|
||
适配器自身不创建后台 worker、不自动建表、不执行 AutoMigrate、不改写默认事务或全表更新保护,也不替业务重试写入。
|
||
|
||
## SQL 日志
|
||
|
||
默认借用当前 context 的 logx,也可显式 `WithLogger(c.Logger)`。每个 ORM 可以使用独立日志实例;不会改动进程全局 Logger。
|
||
|
||
```go
|
||
sqlLog := gormx.DefaultLogConfig()
|
||
sqlLog.Level = "info" // silent / error / warn / info
|
||
sqlLog.SlowThreshold = 300*time.Millisecond
|
||
|
||
orm, err := gormx.MySQL(ctx, c.DB,
|
||
gormx.WithLogger(c.Logger),
|
||
gormx.WithLogConfig(sqlLog),
|
||
gormx.WithConfig(gorm.Config{
|
||
TranslateError: true,
|
||
// NamingStrategy 等原生选项可按项目添加。
|
||
}),
|
||
)
|
||
```
|
||
|
||
默认记录错误和超过 200 ms 的慢查询,忽略 ErrRecordNotFound 的错误事件;`SlowThreshold: 0` 关闭慢查询事件。事件为 `gorm.query`、`gorm.slowQuery`、`gorm.queryFailed`,携带 durationMs、rowsAffected、sql 和 context 中的 requestId/traceId。GORM 的 `Debug()`/`LogMode` 使用副本,不改变其他请求的日志级别;最终输出还受 logx 自身级别控制。
|
||
|
||
默认不插入绑定参数、不记录驱动完整错误文案,只记录错误类别;SQL 正文最多保留 4096 字节,截断后附加省略号。明确开启 `IncludeParameters` / `IncludeErrorDetails` 才输出这些内容。日志不会解析和脱敏直接写在原始 SQL 里的字面量,查询仍应使用占位符。仅需要日志适配的既有 GORM 实例,也可使用 `NewLogger(logger, config)` 返回的原生 `logger.Interface`。
|
||
|
||
`WithConfig` 中若显式设置 `gorm.Config.Logger`,则使用项目自己的 Logger,该 Logger 自行负责脱敏和上下文。配置和 Plugins/ClauseBuilders 容器会复制;用户自定义插件、NamingStrategy 等对象仍由用户保证并发安全。
|
||
|
||
## 其他方言与高级配置
|
||
|
||
MySQL 便捷入口封装了下面的通用入口:
|
||
|
||
```go
|
||
orm, err := gormx.Open(ctx, pool,
|
||
func(conn gorm.ConnPool) gorm.Dialector {
|
||
return mysql.New(mysql.Config{Conn: conn})
|
||
},
|
||
gormx.WithLogger(logger),
|
||
)
|
||
```
|
||
|
||
其他方言可由项目提供相同形式的工厂,必须使用传入的 Conn,不能创建或替换连接池。本版真实数据库验收覆盖 MySQL 8.4,不代表其他数据库已经联调。
|
||
|
||
不能通过 WithConfig 的 ConnPool/Dialector 覆盖此约定。初始化期间不暴露裸 sql.DB;需要它的自定义插件应在 Open 成功后显式注册。创建额外池或替换 ConnPool 的路由插件不属于本适配器的一池所有权范围。
|
||
|
||
PrepareStmt 默认关闭;若显式开启,保留 GORM 的缓存行为,容量、TTL 和缓存清理由项目按 GORM 接口管理。当前固定的 GORM 版本,PreparedStmtDB.Close 只清理语句,不提供停止其缓存清理 goroutine 的公开接口;因此应在服务初始化时创建一次 ORM,避免按请求反复创建。适配器不修改第三方缓存实现,也不宣称解决其生命周期限制。
|
||
|
||
参考:[GORM 现有连接池](https://gorm.io/docs/connecting_to_the_database.html#Existing-database-connection)、[GORM Logger](https://gorm.io/docs/logger.html)。本版验收见 [GORM 验收报告](../../reports/gormAcceptance.md)。
|