Skip to content
tomonclePublic

About

golang orm wrapper.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

18 Commits

Folders and files

Repository files navigation

hummer

Golang ORM 封装,基于 uptrace/bun 提供更简洁的泛型 Service/Repository API、自动迁移与 Schema 同步、分页与条件构建、事务支持、外键管理、以及可配置的日志输出。

安装

  • 运行 go get github.com/tomoncle/hummer

快速开始

  1. 定义模型
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"`
}
  1. 注册模型
import "github.com/tomoncle/hummer/database"

func init() {
    database.RegisteredModel((*SystemConfig)(nil), 10)
}
  1. 初始化数据库
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 = true
type 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)
}

CRUD 与查询(使用 Service)

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) }

Schema 同步与迁移

  • 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

数据初始化(SQL 文件)

  • 配置 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")。

使用 Repository(可选)

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_HOST
  • DB_PORT
  • DB_USERNAME
  • DB_PASSWORD
  • DB_NAME
  • DB_ADMIN_NAME
  • DB_SSLMODE
  • DB_MAX_IDLE_CONNS
  • DB_MAX_OPEN_CONNS
  • DB_CONN_MAX_LIFETIME
  • DB_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
    • SaveOrUpdate Upsert
    • 显式事务提交/回滚
    • 条件过滤与分页
    • 重复键错误映射
    • 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 CONFLICT Upsert
    • 显式事务提交/回滚
    • 条件过滤与分页
    • 主键序列自动补齐
    • 自定义唯一约束名保留
    • 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_TYPE
    • TEST_DB_HOST
    • TEST_DB_PORT
    • TEST_DB_USERNAME
    • TEST_DB_PASSWORD
    • TEST_DB_NAME
  • 人大金仓专项测试读取以下环境变量:
    • KINGBASE_TEST_HOST
    • KINGBASE_TEST_PORT
    • KINGBASE_TEST_USERNAME
    • KINGBASE_TEST_PASSWORD
    • KINGBASE_TEST_DBNAME
    • KINGBASE_TEST_SSLMODE
  • 达梦专项测试读取以下环境变量:
    • DAMENG_TEST_HOST
    • DAMENG_TEST_PORT
    • DAMENG_TEST_USERNAME
    • DAMENG_TEST_PASSWORD
    • DAMENG_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

About

golang orm wrapper.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

⌂ 换网址