2026-05-14 23:42:47 +08:00

@go/db

维护者声明: 本项目由 AI 维护。代码源自 github.com/ssgo/db 的重构,支持现代 Go 特性、内存安全防护、读写分离、全局版本同步及泛型优化。

🎯 设计哲学:约定优于配置

@go/db 遵循“约定优于配置”的设计哲学,旨在通过合理的默认行为和命名约定,简化数据库操作,同时保持强大的功能。

  • 隐式高级功能:版本控制和软删除等高级功能是自动启用的,无需显式配置。
    • 版本控制: 如果一个表包含名为 autoVersion 且类型为 bigint unsigned (ubi) 的字段,Update 和 Insert 操作将自动处理其版本递增和乐观锁。
    • 自动随机 ID: 当字段类型为 char(8/10/12/14) 且为 PRIMARY KEY 或 UNIQUE 时,Insert/Replace 操作若发现该字段为空,将自动填充唯一 ID(优先使用 Redis 分布式 ID)。
    • 智能删除: 如果存在一个名为 [表名]_deleted 的表,Delete 操作将自动执行影子删除(将数据移入该表);否则,执行物理删除。
  • 全局版本号:内置基于 Redis 的全局序列(自动降级为本地 Map),确保分布式环境下 version 绝对单调递增,为可靠的增量同步提供基础。
  • 架构即代码 (DSL):SD 标记现在仅用于建表时自动创建 _deleted 表,而运行时的删除行为由约定决定。
    • 字段类型必填;独立索引使用 a,b U、a,-b I、a I,名称自动生成。字段上的 I/U/TI 创建单字段升序索引;数字分组待 TableDB 联动切换后移除。

📦 安装

go get apigo.cc/go/db

🛠 API 指南

1. 核心方法

  • SetConfig(name, setting string)
    • 动态设置数据库配置(不依赖配置文件),可通过别名获取连接。
  • GetDB(name string, logger *log.Logger) *DB
    • 获取数据库连接实例。name 可以是别名、db.json 中的配置名,也可以是标准 DSN(如 mysql://user:pwd@host:port/db 或 sqlite://test.db)。
    • mysql/mysql:// 默认使用 apigo@127.0.0.1:3306 且不选择业务库;postgres、pgx 及其 URL 简写默认使用当前系统用户连接 127.0.0.1:5432/postgres。省略端口时按协议补齐。
    • 业务库应显式写为 mysql:///apigo 或 pgx:///apigo;三斜线表示省略 host,仍使用本机默认地址和端口。
    • 目标数据库不存在且当前账户具备权限时会自动创建并重连。MySQL/MariaDB 零配置首次启动若尚无 apigo TCP 账户,会通过本机 Unix socket 的管理员身份创建仅限 localhost/127.0.0.1 的空密码 apigo 账户并授权目标库,随后立即改用 TCP;显式 DSN 不触发该引导。
  • Sync(schema string) error
    • 解析 DSL 并同步数据库表结构。用于创建表(包括 _deleted 表)和索引。详见 架构 DSL 指南。

2. 写操作 (返回 *ExecResult)

  • Insert/Replace(table string, data any): 插入或替换数据。若表包含 autoVersion 字段,会自动注入初始版本号。
  • Update(table string, data any, conditions string, args ...any): 更新数据。若表包含 autoVersion 字段,自动递增版本号并应用乐观锁。
  • Delete(table string, conditions string, args ...any): 智能删除。根据是否存在 _deleted 表自动选择物理删除或影子删除。

结果判定 (ExecResult)

res := dbInst.Insert("users", newUser)
if res.Error != nil { /* 发生 SQL 错误 */ }
count := res.Changes() // 受影响行数
id := res.Id()         // 获取自增 ID

3. 读操作 (返回 *QueryResult)

  • Query(query string, args ...any): 执行查询。
  • 结果处理 (QueryResult):
    • 泛型绑定 (推荐): db.To[T](res), db.ToSlice[T](res)
    • KV 映射: res.ToKV(&mapObj) 将前两列自动转为 Map。
    • 快捷取值: IntOnR1C1(), StringOnR1C1(), MapOnR1(), StringsOnC1() 等。
    • 错误感知: 所有结果方法都会同步更新 res.Error,可链式调用后统一判断。

🔐 安全与加密

我们极致注重数据安全:

  • 密码防御: 内存中的数据库密码受 safe.SafeBuf 保护,防止通过内存 Dump 获取明文。
  • 配置加密: 建议在 db.json 中使用密文存储敏感信息。
  • TODO: sskey 集成: 计划引入 sskey 工具,实现生产环境密钥的统一托管与自动解密。

🏗 架构即代码 (DSL 示例)

我们鼓励通过 DSL 定义表结构,实现“修改代码即修改表”。

schema := `
== Default ==
users SD // 用户表,开启影子删除
  id i AI // 自增 ID
  name v50 U // 字符串(50),唯一索引
  autoVersion ubi // 自动版本号
  status ti // 状态 (TinyInt)
`
dbInst.Sync(schema) // 自动创建 users 和 users_deleted 表及索引

4. 事务

SQLite FTS5 使用数据表的整数 rowid 关联全文记录,业务主键可为字符串。Schema 同步会迁移旧的业务主键关联触发器,并在事务中回填已有 token;字段不变的同步也会检查该迁移。LIKE 到 FTS 的查询映射同样使用 rowid。

SQLite 单表 Schema 同步将字段、FTS 表和触发器变更放在同一事务中。删除 token 列前解除旧触发器引用;全文字段集合变化时重建并回填 FTS,移除最后一个全文字段时清理 FTS 表和触发器。事务回滚保留原有字段和全文检索。

SQLite 的 tx.Sync(schema) 可将物理 Schema 修改与同事务的数据/元数据修改一起提交。出错后调用方必须回滚;底层 Schema 缓存仅在成功提交后失效。其他数据库暂不支持该事务内同步入口。

tx := dbInst.Begin()
if tx.Error != nil { /* 处理错误 */ }
defer tx.CheckFinished() // 自动处理未提交的 Rollback

tx.Insert("users", newUser)
if tx.Error == nil {
    tx.Commit()
}

📖 详细文档

Description
High-performance database abstraction layer for apigo.cc/go
Readme 405 KiB
Languages
Go 100%