告别 GORM 的“魔法字符串”和“事务满天飞”:我开源了一个强类型查询构建库

大家好,

平时在用 Go 和 GORM 写业务代码时,不知道大家有没有经历过以下几个极其破坏开发体验的"痛点":

  1. 脆弱的魔法字符串 :写着 db.Where("age > ? AND user_name LIKE ?", 18, "%张三%"),一旦数据库字段名改了,编译期根本发现不了,直接在运行时原地爆炸。
  2. 连表查询的字段歧义 :当你 Joins 了好几张表,一旦有多张表都有 idcreated_at,直接给你报 ambiguous column name,只能痛苦地手拼 users.id
  3. 恶心的事务传递 :为了保证事务,不得不把 tx *gorm.DB 作为参数在 Service 层和 Repo 层之间传来传去,极其破坏架构的整洁度。
  4. 查询条件互相污染 :复用同一个基础查询构建器时,底层的 Slice 被 append 污染,导致派生查询带上了莫名其妙的 WHERE 条件。

为了彻底解决这些痛点,我把团队内部沉淀的脚手架剥离、重构,开源了这个纯粹的通用依赖库:GORM Query

🔗 开源地址github.com/im-wmkong/g...

✨ 这个库能帮你做什么?

1. 极致的强类型查询(告别魔法字符串)

只需要在现有的 Model 上加一行注释,内置的代码生成器就能自动生成强类型的属性字典(完美兼容 gorm.Model)。

以前你这样写:

go 复制代码
db.Where("status = ? AND age >= ?", 1, 18).Find(&users)

现在你可以这样写(享受 IDE 的完美代码提示,彻底消灭拼写错误):

go 复制代码
qb := query.New().Where(
    model.UserProps.Status.Eq(1),
    model.UserProps.Age.Gte(18),
)
qb.Apply(db).Find(&users)

2. 丝滑解决 JOIN 字段歧义

生成的属性自带 .Alias() 方法,利用底层私有常量拼接,保证幂等性的同时,极其优雅地解决连表前缀问题:

go 复制代码
// 直接生成带前缀的 SQL: u.age > 18 AND p.city = 'Beijing'
qb := query.New().Joins("JOIN profiles p ON u.id = p.user_id").Where(
    model.UserProps.Alias("u").Age.Gt(18),
    model.ProfileProps.Alias("p").City.Eq("Beijing"),
)

3. 隐式上下文事务(让 Service 与 Repo 彻底解耦)

库内置了基于 context.Context 的事务管理器和 repo.BaseRepository[T] 泛型基类。

配合依赖注入,你的 Service 层可以保持极致的干净,一行 DB 相关的代码都不用写

go 复制代码
// 业务代码完全不需要知道底层 gorm.DB 的存在
func (s *UserService) CreateUserAndProfile(ctx context.Context, user *model.User, profile *model.Profile) error {
    // 一键开启事务,向下文透传
    return s.tm.Transaction(ctx, func(txCtx context.Context) error {
        
        // 底层的 BaseRepository 会自动感知 txCtx 并使用事务连接!
        if err := s.userRepo.Create(txCtx, user); err != nil {
            return err 
        }

        profile.UserID = user.ID
        // 如果这里失败,上面的 Create 会自动回滚
        if err := s.profileRepo.Create(txCtx, profile); err != nil {
            return err
        }
        
        return nil
    })
}

4. 安全的查询克隆 (防污染)

提供 .Clone() 方法深度拷贝查询条件,基础查询随心复用:

go 复制代码
baseQuery := query.New().Where(UserProps.Status.Eq(1))
adultsQuery := baseQuery.Clone().Where(UserProps.Age.Gte(18)) 
minorsQuery := baseQuery.Clone().Where(UserProps.Age.Lt(18)) // 安全独立,互不污染

🛠️ 如何快速体验?

只需要两步:

第一步:引入库并添加生成指令

go 复制代码
//go:generate go run [github.com/im-wmkong/gorm-query/cmd/gen-props@latest](https://github.com/im-wmkong/gorm-query/cmd/gen-props@latest) -type=User

第二步:执行生成命令 在终端运行 go generate ./...,然后就可以开始享受丝滑的编码体验了!

如果你也追求代码的优雅与整洁,欢迎去 GitHub 看看源码。如果觉得这个设计思路对你的日常开发有帮助,求个 Star ⭐️ 支持一下!也极其欢迎各种 Issue 和 PR 一起共建。

仓库地址:github.com/im-wmkong/g...

相关推荐
iPadiPhone2 小时前
性能优化的“双刃剑”:MySQL 查询缓存深度架构解析与面试复盘
java·后端·mysql·缓存·面试·性能优化
Meta392 小时前
SpringBoot通过kt-connect+kubectl进行本地调试k8s服务
spring boot·后端·kubernetes
杰杰7982 小时前
深入理解 Django REST Framework 的 Serializer(上)
后端·python·django
tant1an2 小时前
Spring Boot 进阶之路:热部署机制 + 配置高级特性详解
java·spring boot·后端
xiaoye37082 小时前
如何在Spring中使用注解解决线程并发问题?
java·后端·spring
future02102 小时前
Spring IOC启动全流程解密
java·后端·spring·ioc
太阳神LoveU2 小时前
Spring Boot 4.0.3和3.X的各个版本主要功能差别和优劣势对比
java·spring boot·后端
zhoupenghui1682 小时前
golang 锁实现原理与解析&锁机制(sync)种类与举例说明以及其使用场景
开发语言·后端·golang·mutex·wait·lock·sync
掘金者阿豪2 小时前
从“多库掣肘”到“一库平川”:金仓KingbaseES的融合数据库深度体验
后端