Juice: 为 Go 开发者打造的 MyBatis 风格高性能 SQL 映射器
你是否在 Go 的数据访问层开发中遇到过这些困境?
- 原生
database/sql:虽然灵活、直接,但for rows.Next()和rows.Scan(...)的模板代码写到手软,面对复杂的动态查询,只能在代码里痛苦地拼接字符串,一不小心就可能引入 SQL 注入的风险。 - 重量级 ORM (如 GORM):入门简单,链式调用一时爽,但随着业务复杂度的提升,你开始不确定它生成的 SQL 是什么样子。过度封装的“魔法”让你失去了对 SQL 的掌控力,性能调优也变得束手无策。
- 轻量级封装 (如
sqlx):它极大地改善了结果映射的体验,但对于动态 SQL 的支持依然有限,SQL 语句还是散落在业务逻辑中,难以集中管理和维护。
如果你对以上场景感同身受,那么我们很荣幸地向你介绍 Juice——一个为你量身打造的、受 MyBatis 启发的 Go SQL 映射器框架。
GitHub 地址: https://github.com/go-juicedev/juice
什么是 Juice?
Juice 是一个轻量级、零依赖、高性能的 SQL 映射器。它的核心哲学是将 SQL 从业务逻辑代码中彻底分离,让你像 MyBatis 一样,在一个独立的 XML 文件中管理所有的 SQL。
这让你能够:
- SQL-First: 专注于编写高质量、高性能的 SQL。
- 关注点分离: Go 代码负责业务逻辑,XML 负责数据查询,职责清晰,维护轻松。
- 拥抱变化: 当需要修改查询逻辑或进行 SQL 优化时,你不再需要重新编译整个应用。
✨ 核心特性一览
- 熟悉的 MyBatis 风格:如果你有 MyBatis 的使用经验,上手 Juice 将会毫无门槛。通过简洁的 XML 配置,将接口方法与 SQL 语句优雅地映射起来。
- 强大的动态 SQL:这是 Juice 区别于其他库的“杀手级特性”。使用
<if>,<foreach>(即将支持) 等标签,你可以在 XML 中轻松构建复杂的动态查询,告别在 Go 代码中繁琐的if-else逻辑和字符串拼接。 - 零依赖、极致轻量: Juice 的
go.mod中没有任何外部依赖。它是一个纯粹、自包含的库,你可以轻松地将其集成到任何项目中,而无需担心依赖冲突。 - 为性能而生: Juice 深度集成并优化了
database/sql。它内置了对预编译语句 (PreparedStatement) 和批量处理的支持,在处理高并发和大数据量场景时,能提供卓越的性能。 - 现代 Go 特性,类型安全: 借助 Go 泛型,Juice 提供了类型安全的结果绑定 API,如
juice.QueryContext[T],让你的代码更健壮、更易读。 - 强大的扩展能力: 通过内置的中间件 (Middleware) 机制,你可以轻松地为 SQL 执行过程加入日志、链路追踪 (Tracing)、多租户过滤等功能,实现 AOP 式的逻辑注入。
- 无与伦比的开发体验: 我们为 JetBrains IDE (GoLand, IDEA) 开发了专属的 Juice 插件!它提供了从 Go 代码到 XML 定义的无缝跳转、语法高亮和自动补全,让你的开发体验如丝般顺滑。
🚀 快速上手
体验 Juice 的优雅仅需三步:
1. 定义配置文件 config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//juice.org//DTD Config 1.0//EN"
"https://raw.githubusercontent.com/eatmoreapple/juice/main/config.dtd">
<configuration>
<environments default="dev">
<environment id="dev">
<dataSource>sqlite.db</dataSource>
<driver>sqlite3</driver>
</environment>
</environments>
<mappers>
<mapper resource="mappers.xml"/>
</mappers>
</configuration>
2. 编写 SQL 映射文件 mappers.xml:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//juice.org//DTD Config 1.0//EN"
"https://raw.githubusercontent.com/eatmoreapple/juice/main/mapper.dtd">
<mapper namespace="main.UserRepository">
<select id="FindUserByID">
SELECT id, name FROM users
<if test="id != nil">
WHERE id = #{id}
</if>
</select>
</mapper>
3. 编写 Go 代码:
package main
import (
"context"
"fmt"
"github.com/go-juicedev/juice"
_ "github.com/mattn/go-sqlite3"
)
type User struct {
ID int64
Name string
}
// UserRepository 定义了与 mapper.xml 中 namespace="main.UserRepository" 对应的接口
type UserRepository interface {
FindUserByID(ctx context.Context, id any) (*User, error)
}
// 定义一个实现,方法名与 select 标签的 id 对应
type UserRepositoryImpl struct{}
func (r *UserRepositoryImpl) FindUserByID(ctx context.Context, id any) (*User, error) {
// 使用泛型方法,将查询结果自动绑定到 User 结构体
return juice.QueryContext[*User](ctx, (*UserRepository)(nil).FindUserByID, id)
}
func main() {
// 初始化引擎
engine, err := juice.NewFromFile("config.xml")
if err != nil {
panic(err)
}
defer engine.Close()
// 将引擎注入上下文
ctx := juice.ContextWithManager(context.Background(), engine)
repo := &UserRepositoryImpl{}
// 调用查询
user, err := repo.FindUserByID(ctx, 1)
if err != nil {
panic(err)
}
fmt.Printf("Found user: %+v\n", user)
}
看,就是这么简单!SQL 和 Go 代码实现了完美分离。
为何选择 Juice?
| 特性 | 原生 database/sql |
sqlx |
GORM |
Juice |
|---|---|---|---|---|
| SQL 控制力 | 最高 | 高 | 低(隐藏SQL) | 最高 |
| 动态 SQL | 手动拼接 | 有限 | 链式API | 强大 (XML) |
| SQL与代码分离 | 否 | 否 | 是 | 是(核心特性) |
| 学习曲线 | 低 | 低 | 中 | 低 (对MyBatis用户) |
| 性能开销 | 最低 | 极低 | 有 | 极低 |
| 依赖 | 无 | 无 | 有 | 无 |
Juice 精准地切入了原生 sql 和重量级 ORM 之间的市场空白,为你提供了两全其美的最佳选择。

浙公网安备 33010602011771号