golang类型安全的SQL查询框架插件库jet的使用

Golang类型安全的SQL查询框架插件库Jet的使用

Jet是一个高效、高性能的数据库访问完整解决方案,它结合了类型安全的SQL构建器、代码生成和自动查询结果映射功能。

主要特性

  1. 自动生成的类型安全SQL构建器,支持以下语句:

    • SELECT, SELECT_JSON (DISTINCT, FROM, WHERE, GROUP BY等)
    • INSERT (VALUES, MODEL, MODELS等)
    • UPDATE (SET, MODEL, WHERE等)
    • DELETE (WHERE, ORDER_BY等)
    • LOCK (IN, NOWAIT等)
    • WITH
  2. 自动生成的数据模型类型 - 映射到数据库类型(表、视图或枚举)的Go类型

  3. 查询执行与结果映射 - 可将结果映射到任意目标结构

支持的数据库

  • PostgreSQL
  • MySQL
  • SQLite

注意:Jet不是ORM

快速开始

安装

添加Jet作为依赖:

go get -u github.com/go-jet/jet/v2

安装Jet生成器:

go install github.com/go-jet/jet/v2/cmd/jet@latest

生成SQL构建器和模型类型

假设我们有一个PostgreSQL数据库,使用以下命令生成代码:

jet -dsn=postgresql://user:pass@localhost:5432/jetdb?sslmode=disable -schema=dvds -path=./.gen

编写SQL查询

import (
    . "github.com/go-jet/jet/v2/examples/quick-start/.gen/jetdb/dvds/table"
    . "github.com/go-jet/jet/v2/postgres"

    "github.com/go-jet/jet/v2/examples/quick-start/.gen/jetdb/dvds/enum"
    "github.com/go-jet/jet/v2/examples/quick-start/.gen/jetdb/dvds/model"  
)

stmt := SELECT(
    Actor.ActorID, Actor.FirstName, Actor.LastName, Actor.LastUpdate,
    Film.AllColumns,                                                  
    Language.AllColumns.Except(Language.LastUpdate),
    Category.AllColumns,
).FROM(
    Actor.
        INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)).  
        INNER_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID)).          
        INNER_JOIN(Language, Language.LanguageID.EQ(Film.LanguageID)).
        INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
        INNER_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)),
).WHERE(
    Language.Name.EQ(Char(20)("English")).             
        AND(Category.Name.NOT_EQ(Text("Action"))).  
        AND(Film.Length.GT(Int32(180))).
        AND(Film.Rating.NOT_EQ(enum.MpaaRating.R)),              
).ORDER_BY(
    Actor.ActorID.ASC(),
    Film.FilmID.ASC(),
)

执行查询并存储结果

var dest []struct {
    model.Actor
    
    Films []struct {
        model.Film
        
        Language    model.Language
        Categories  []model.Category
    }
}

err := stmt.Query(db, &dest)
handleError(err)

完整示例

以下是一个完整的示例,展示如何使用Jet进行类型安全的SQL查询:

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    
    // 导入生成的表和模型
    . "github.com/go-jet/jet/v2/examples/quick-start/.gen/jetdb/dvds/table"
    . "github.com/go-jet/jet/v2/postgres"
    
    "github.com/go-jet/jet/v2/examples/quick-start/.gen/jetdb/dvds/enum"
    "github.com/go-jet/jet/v2/examples/quick-start/.gen/jetdb/dvds/model"
    
    _ "github.com/lib/pq"
)

func main() {
    // 连接数据库
    db, err := sql.Open("postgres", "postgresql://user:pass@localhost:5432/jetdb?sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // 构建查询
    stmt := SELECT(
        Actor.ActorID, Actor.FirstName, Actor.LastName,
        Film.AllColumns,
        Language.AllColumns.Except(Language.LastUpdate),
        Category.AllColumns,
    ).FROM(
        Actor.
            INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)).
            INNER_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID)).
            INNER_JOIN(Language, Language.LanguageID.EQ(Film.LanguageID)).
            INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
            INNER_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)),
    ).WHERE(
        Language.Name.EQ(Char(20)("English")).
            AND(Category.Name.NOT_EQ(Text("Action"))).
            AND(Film.Length.GT(Int32(180))).
            AND(Film.Rating.NOT_EQ(enum.MpaaRating.R)),
    ).ORDER_BY(
        Actor.ActorID.ASC(),
        Film.FilmID.ASC(),
    )

    // 定义结果结构
    var dest []struct {
        model.Actor
        
        Films []struct {
            model.Film
            
            Language    model.Language
            Categories  []model.Category
        }
    }

    // 执行查询
    err = stmt.Query(db, &dest)
    if err != nil {
        log.Fatal(err)
    }

    // 打印结果
    jsonText, _ := json.MarshalIndent(dest, "", "\t")
    fmt.Println(string(jsonText))
}

优势

  1. 开发速度 - 类型安全的SQL代码自动完成,减少错误
  2. 执行速度 - 避免N+1查询问题,单次查询获取复杂结果
  3. 错误检测 - 编译时捕获类型不匹配等问题

Jet是一个强大的工具,可以帮助开发者更高效、更安全地构建数据库查询。


更多关于golang类型安全的SQL查询框架插件库jet的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang类型安全的SQL查询框架插件库jet的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 类型安全的 SQL 查询框架 - Jet 使用指南

Jet 是一个类型安全的 SQL 查询构建器,它通过代码生成方式为你的数据库结构创建类型安全的模型,避免了手写 SQL 字符串的繁琐和潜在错误。

Jet 核心特性

  1. 完全类型安全
  2. 支持多种数据库(PostgreSQL, MySQL, MariaDB, CockroachDB, SQLite)
  3. 自动代码生成
  4. 支持复杂查询(JOIN, 子查询等)
  5. 编译时检查 SQL 语法

安装 Jet

go get -u github.com/go-jet/jet/v2

基本使用示例

1. 生成模型代码

首先需要从数据库生成模型代码:

jet -source=postgres -host=localhost -port=5432 -user=postgres -password=password -dbname=mydb -path=./gen

这会根据数据库结构生成对应的模型代码到 ./gen 目录。

2. 基本查询示例

package main

import (
	"context"
	"fmt"
	"log"
	
	"github.com/go-jet/jet/v2/postgres"
	_ "github.com/lib/pq"
	
	// 导入生成的模型
	"./gen/mydb/table"
	"./gen/mydb/model"
)

func main() {
	db, err := sql.Open("postgres", "postgres://user:password@localhost:5432/mydb?sslmode=disable")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// 简单查询
	stmt := postgres.SELECT(
		table.Users.ID,
		table.Users.Name,
		table.Users.Email,
	).FROM(
		table.Users,
	).WHERE(
		table.Users.Age.GT(postgres.Int(18)),
	).ORDER_BY(
		table.Users.Name.ASC(),
	)

	var users []model.User
	err = stmt.QueryContext(context.Background(), db, &users)
	if err != nil {
		log.Fatal(err)
	}

	for _, user := range users {
		fmt.Printf("ID: %d, Name: %s, Email: %s\n", user.ID, user.Name, user.Email)
	}
}

3. 复杂查询示例

// 带JOIN的查询
stmt := postgres.SELECT(
	table.Users.ID,
	table.Users.Name,
	table.Orders.ID.AS("order_id"),
	table.Orders.Amount,
).FROM(
	table.Users.
		INNER_JOIN(table.Orders, table.Orders.UserID.EQ(table.Users.ID)),
).WHERE(
	table.Users.Status.EQ(postgres.String("active")),
).LIMIT(10)

var results []struct {
	model.User
	OrderID int64   `alias:"order_id"`
	Amount  float64
}

err = stmt.QueryContext(context.Background(), db, &results)

4. 插入数据

// 单条插入
stmt := table.Users.INSERT(
	table.Users.Name,
	table.Users.Email,
	table.Users.Age,
).VALUES(
	"John Doe",
	"john@example.com",
	30,
)

_, err := stmt.ExecContext(context.Background(), db)

// 批量插入
usersToInsert := []model.User{
	{Name: "Alice", Email: "alice@example.com", Age: 25},
	{Name: "Bob", Email: "bob@example.com", Age: 28},
}

var columns = postgres.ColumnList{
	table.Users.Name,
	table.Users.Email,
	table.Users.Age,
}

stmt = table.Users.INSERT(columns).
	MODELS(usersToInsert)

_, err = stmt.ExecContext(context.Background(), db)

5. 更新数据

stmt := table.Users.UPDATE().
	SET(
		table.Users.Age.SET(table.Users.Age.ADD(postgres.Int(1))),
		table.Users.UpdatedAt.SET(postgres.CurrentTimestamp()),
	).WHERE(
		table.Users.ID.EQ(postgres.Int(1)),
	)

_, err := stmt.ExecContext(context.Background(), db)

6. 删除数据

stmt := table.Users.DELETE().
	WHERE(
		table.Users.ID.EQ(postgres.Int(1)),
	)

_, err := stmt.ExecContext(context.Background(), db)

高级功能

1. 子查询

subQuery := postgres.SELECT(
	table.Orders.UserID,
	postgres.COUNT(table.Orders.ID).AS("order_count"),
).FROM(
	table.Orders,
).GROUP_BY(
	table.Orders.UserID,
).AsTable("order_counts")

stmt := postgres.SELECT(
	table.Users.ID,
	table.Users.Name,
	subQuery.Column("order_count"),
).FROM(
	table.Users.
		LEFT_JOIN(subQuery, subQuery.Column("user_id").EQ(table.Users.ID)),
)

2. 事务支持

tx, err := db.Begin()
if err != nil {
	log.Fatal(err)
}
defer tx.Rollback()

// 在事务中执行多个操作
_, err = table.Users.INSERT(...).ExecContext(ctx, tx)
_, err = table.Orders.INSERT(...).ExecContext(ctx, tx)

if err != nil {
	log.Fatal(err)
}

err = tx.Commit()
if err != nil {
	log.Fatal(err)
}

3. 自定义扫描

var customResults []struct {
	model.User
	OrderCount int `sql:"order_count"`
}

err = stmt.QueryContext(ctx, db, &customResults)

性能考虑

  1. Jet 在编译时生成所有 SQL 构建代码,运行时开销极小
  2. 查询构建过程无反射使用
  3. 最终生成的 SQL 语句与手写 SQL 性能相同

总结

Jet 提供了类型安全的 SQL 查询构建方式,通过代码生成避免了手写 SQL 字符串的错误,同时保持了良好的性能。它特别适合需要严格类型检查的中大型项目,可以显著提高数据库操作的可靠性和开发效率。

对于简单的 CRUD 操作,Jet 可能显得有点重,但对于复杂查询和需要严格类型安全的场景,Jet 是一个非常优秀的选择。

回到顶部