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。

这让你能够:

  1. SQL-First: 专注于编写高质量、高性能的 SQL。
  2. 关注点分离: Go 代码负责业务逻辑,XML 负责数据查询,职责清晰,维护轻松。
  3. 拥抱变化: 当需要修改查询逻辑或进行 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 之间的市场空白,为你提供了两全其美的最佳选择。

posted @ 2025-11-20 10:04  Ivy丶  阅读(7)  评论(0)    收藏  举报