Golang ORM 封装,基于 uptrace/bun 提供更简洁的泛型 Service/Repository API、自动迁移与 Schema 同步、分页与条件构建、事务支持、外键管理、以及可配置的日志输出。
- 运行
go get github.com/tomoncle/hummer
- 定义模型
package model
import (
"github.com/uptrace/bun"
)
type SystemConfig struct {
bun.BaseModel `bun:"table:system_config"`
ID int64 `bun:"id,pk,autoincrement"`
Name string `bun:"name,unique:uk_system_config_name,notnull,type:varchar(64)"`
Value string `bun:"value,type:text" hummer:"rename:cfg_value"`
}- 注册模型
import "github.com/tomoncle/hummer/database"
func init() {
database.RegisteredModel((*SystemConfig)(nil), 10)
}- 初始化数据库
package main
import (
"context"
"github.com/tomoncle/hummer/database"
)
func main() {
cfg := &database.Config{
ConnectionConfig: database.ConnectionConfig{
Type: "mysql", // 支持 mysql / dameng / postgres / sqlite / kingbase
Host: "127.0.0.1",
Port: 3306,
Username: "root",
Password: "",
DBName: "who",
EnableQueryLog: true,
},
}
_, err := database.InitDB(cfg)
if err != nil { panic(err) }
defer func() { _ = database.CloseDB() }()
_ = context.Background()
}仓库提供了完整注释版 TOML 示例:configs/hummer.example.toml。
Hummer 的数据库入口是 database.Config。如果你希望直接使用框架提供的 TOML 加载器,可以这样写:
import (
hummerconfig "github.com/tomoncle/hummer/config"
"github.com/tomoncle/hummer/database"
)
cfg, err := hummerconfig.LoadToml("configs/hummer.toml")
if err != nil {
panic(err)
}
cfg.ApplyLogger() // 可选:应用 [logger] 配置,需要在创建 logger 前调用。
_, err = database.InitDB(cfg.DatabaseConfig())
if err != nil {
panic(err)
}如果你的项目已有自己的大配置文件,可以把 Hummer 配置作为子结构体嵌进去:
name = "example-api"
[hummer.connection_config]
type = "mysql"
host = "127.0.0.1"
port = 3306
username = "root"
password = "change-me"
dbname = "example"
conn_max_lifetime = "1h"
connect_timeout = "10s"
[hummer.data_migrate_config]
enable_migrate_on_startup = true
enable_schema_sync = true
schema_sync_dry_run = truetype AppConfig struct {
Name string `toml:"name"`
Hummer hummerconfig.HummerConfig `toml:"hummer"`
}
var appCfg AppConfig
if err := hummerconfig.LoadTomlInto("configs/app.toml", &appCfg); err != nil {
panic(err)
}
_, err := database.InitDB(appCfg.Hummer.DatabaseConfig())
if err != nil {
panic(err)
}import (
"context"
"github.com/tomoncle/hummer"
"github.com/tomoncle/hummer/types"
)
svc := hummer.NewService[SystemConfig]()
ctx := context.Background()
// 创建
_ = svc.Save(ctx, &SystemConfig{Name: "site_title", Value: "Hummer"})
// 获取
cfg, _ := svc.Get(ctx, 1)
// 列表(条件)
cb := types.NewConditionBuilder().AND("name = ?", "site_title").Condition()
list, _ := svc.List(ctx, cb)
// 分页与排序
page := types.NewPageRequest(1, 10, cb, []string{"id DESC"})
pg, _ := svc.Page(ctx, page)
// 条件查询
raw, _ := svc.Query(ctx, "name = ?", "site_title")import "github.com/tomoncle/hummer/database"
db := database.GetDB()
tx, err := db.BeginTx(ctx, nil)
if err != nil { panic(err) }
defer func() { _ = tx.Rollback() }()
err = hummer.NewService[SystemConfig]().SaveWithTx(ctx, tx, &SystemConfig{Name: "k", Value: "v"})
if err != nil { panic(err) }
if err := tx.Commit(); err != nil { panic(err) }EnableMigrateOnStartup:启动时执行迁移(创建迁移表,执行已注册迁移)。EnableSchemaSync:根据bun标签推断目标表结构并与现有结构比对,同步列与索引。- 列标签支持:
type:...、notnull、default:...、pk、autoincrement、unique[:name]。 - 重命名列:通过
hummer:"rename:old_name"标记,用于在同步时将旧列改为新列。
- 通过配置启用:
DataMigrateConfig.EnableForeignKey = true。 - 支持从 YAML 文件加载外键:
DataMigrateConfig.ForeignKeyFile。
示例 foreign_keys.yaml:
foreign_keys:
- table: system_config
column: user_id
reference_table: users
reference_column: id
on_delete: CASCADE
on_update: NO ACTION
constraint_name: fk_system_config_user- 配置
DataInitConfig.Filepath为 SQL 根目录,Env为环境名(如development)。 DataInitConfig.Environment是旧的长字段兼容写法;如果Env和Environment同时配置,Environment优先。- 初始化时会先读取公共 SQL 目录:
DataInitConfig.Filepath + "/common"。 - 然后读取环境 SQL 目录:
DataInitConfig.Filepath + "/envs/" + DataInitConfig.Env。 - 为兼容旧项目,如果
envs/<env>不存在,会回退读取旧目录:DataInitConfig.Filepath + "/environments/" + DataInitConfig.Env。 - 如果
envs/<env>和environments/<env>同时存在,只读取envs/<env>,避免同名 SQL 被重复发现或data_sync迁移记录冲突。 - 执行优先级:
common目录先执行,环境目录后执行;同一目录内按文件名前缀数字排序,没有数字前缀时默认排到后面。 - 执行:
database.InitData()或database.InitDataWithSQL("development")。
import "github.com/tomoncle/hummer/repository"
repo := repository.NewRepository[SystemConfig](database.GetDB())
entities, _ := repo.List(ctx, cb)
pagination, _ := repo.Page(ctx, types.NewPageRequest(1, 20, nil, nil))import "github.com/tomoncle/hummer/utils"
utils.ConfigureFileLog("logs", 7, false) // 文件日志按天切分
utils.ConfigureFileLogFormat("json") // 文件日志格式:json/text
utils.ConfigureConsoleLogFormat("text") // 控制台日志格式:json/text
utils.ConfigureLogLevel("info") // 统一日志级别
lg := utils.NewLogger("APP")
lg.Info("service started")hs := database.GetHealthStatus(ctx) // { healthy, connected, response_time, ... }
stats := database.GetDatabaseStats() // 连接池统计DB_HOSTDB_PORTDB_USERNAMEDB_PASSWORDDB_NAMEDB_ADMIN_NAMEDB_SSLMODEDB_MAX_IDLE_CONNSDB_MAX_OPEN_CONNSDB_CONN_MAX_LIFETIMEDB_ENABLE_QUERY_LOG
- MySQL、Dameng(达梦,原生驱动 / MySQL 兼容模式)、PostgreSQL、SQLite、KingbaseES(人大金仓)
- 配置
ConnectionConfig.Type = "dameng"时,框架会优先走达梦原生 Go 驱动github.com/fengzehao/dm-go-driver,连接串形如dm://user:password@host:port/dbname。 - 如需继续使用达梦 MySQL 兼容协议,可显式配置
ConnectionConfig.Type = "dameng-mysql"。 - 当前 Bun 没有原生达梦方言,框架在 SQL 生成层仍复用 MySQL 家族能力,并额外对达梦的连接、冲突写入、错误识别与自动建库探测做了专门处理。
AutoCreate=true时,原生达梦会按DBName作为目标schema处理:- 通过
SYS.SYSOBJECTS中TYPE$='SCH'探测 schema 是否存在 - 不存在时执行
CREATE SCHEMA "<DBName>" AUTHORIZATION "<Username>" - 这条路径不再依赖
INFORMATION_SCHEMA或SHOW DATABASES
- 通过
- 达梦原生驱动路径默认连接字符集为
utf8;如环境需要,可通过ConnectionConfig.Charset覆盖连接参数与自动建库字符集。 - 如需通过指定管理库执行达梦自动建库探测,可设置
ConnectionConfig.AdminDBName或环境变量DB_ADMIN_NAME。 - 当前仓库内已提供达梦专项集成测试入口,覆盖:
- 连接初始化与迁移
- 基础 CRUD
SaveOrUpdateUpsert- 显式事务提交/回滚
- 条件过滤与分页
- 重复键错误映射
NOT NULL/ 外键 / CHECK 约束错误映射- Schema Sync 的列重命名 / 新增列 / 唯一索引 / 列定义修改
示例配置:
cfg := &database.Config{
ConnectionConfig: database.ConnectionConfig{
Type: "dameng",
Host: "127.0.0.1",
Port: 5236,
Username: "SYSDBA",
Password: "password",
DBName: "example",
AutoCreate: false,
},
}- 配置
ConnectionConfig.Type = "kingbase"即可启用人大金仓支持。 - 底层驱动使用
gitea.com/kingbase/gokb,Bun 方言复用 PostgreSQL 家族能力。 AutoCreate=true时,默认连接管理库security检查并创建目标库;如环境不同,可通过ConnectionConfig.AdminDBName或环境变量DB_ADMIN_NAME覆盖。- 已验证能力:
- 连接初始化
- 自动迁移
- Schema 同步
- 基础 CRUD
ON CONFLICTUpsert- 显式事务提交/回滚
- 条件过滤与分页
- 主键序列自动补齐
- 自定义唯一约束名保留
- SQL 错误识别:重复键 / 外键 / CHECK 约束
AutoCreate管理库探测失败时回退连接业务库
已知行为:
- 当模型字段带有
default:...标签时,Save/Create在零值场景下可能沿用数据库默认值,而不是显式写入零值。 - 例如:
bool字段声明为bun:"enabled,notnull,default:true"时,Enabled: false在插入阶段可能落成数据库默认值true。 - 对这类字段,如果需要显式覆盖默认值,建议使用
Update或SaveOrUpdate。 - 该行为来自 Bun 对带默认值字段的插入策略,不是人大金仓独有差异;MySQL/PostgreSQL/SQLite 也可能表现一致。
- 在当前已验证的 Kingbase8 环境中,向
NOT NULL varchar列显式插入NULL时,数据库可能不会抛出NOT NULL约束异常,而是将值折算为空字符串""。 - 因此,
NOT NULL违规是否能稳定映射为NotNullViolationErr,取决于实际 Kingbase 实例参数和行为模式,不能假定所有环境都与 PostgreSQL 完全一致。 - 若
AutoCreate所需的管理库或系统视图不可访问,框架会记录告警并继续尝试连接业务库,不会因为自动建库探测失败而直接中断初始化。
示例配置:
cfg := &database.Config{
ConnectionConfig: database.ConnectionConfig{
Type: "kingbase",
Host: "127.0.0.1",
Port: 4321,
Username: "system",
Password: "password",
DBName: "example",
AdminDBName: "security",
SSLMode: "disable",
},
}- 通用集成测试读取以下环境变量:
TEST_DB_TYPETEST_DB_HOSTTEST_DB_PORTTEST_DB_USERNAMETEST_DB_PASSWORDTEST_DB_NAME
- 人大金仓专项测试读取以下环境变量:
KINGBASE_TEST_HOSTKINGBASE_TEST_PORTKINGBASE_TEST_USERNAMEKINGBASE_TEST_PASSWORDKINGBASE_TEST_DBNAMEKINGBASE_TEST_SSLMODE
- 达梦专项测试读取以下环境变量:
DAMENG_TEST_HOSTDAMENG_TEST_PORTDAMENG_TEST_USERNAMEDAMENG_TEST_PASSWORDDAMENG_TEST_DBNAME
- 未提供这些环境变量时,相关测试会自动跳过。
运行示例:
$env:KINGBASE_TEST_HOST="127.0.0.1"
$env:KINGBASE_TEST_PORT="4321"
$env:KINGBASE_TEST_USERNAME="kingbase"
$env:KINGBASE_TEST_PASSWORD="your_password"
$env:KINGBASE_TEST_DBNAME="test"
go test ./tests -run TestKingbase -v$env:DAMENG_TEST_HOST="127.0.0.1"
$env:DAMENG_TEST_PORT="5236"
$env:DAMENG_TEST_USERNAME="SYSDBA"
$env:DAMENG_TEST_PASSWORD="your_password"
$env:DAMENG_TEST_DBNAME="test"
go test ./tests -run TestDameng -v- ORM:
github.com/uptrace/bun