golang类型安全的SQL查询框架插件库jet的使用
Golang类型安全的SQL查询框架插件库Jet的使用
Jet是一个高效、高性能的数据库访问完整解决方案,它结合了类型安全的SQL构建器、代码生成和自动查询结果映射功能。
主要特性
-
自动生成的类型安全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
-
自动生成的数据模型类型 - 映射到数据库类型(表、视图或枚举)的Go类型
-
查询执行与结果映射 - 可将结果映射到任意目标结构
支持的数据库
- 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))
}
优势
- 开发速度 - 类型安全的SQL代码自动完成,减少错误
- 执行速度 - 避免N+1查询问题,单次查询获取复杂结果
- 错误检测 - 编译时捕获类型不匹配等问题
Jet是一个强大的工具,可以帮助开发者更高效、更安全地构建数据库查询。
更多关于golang类型安全的SQL查询框架插件库jet的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang类型安全的SQL查询框架插件库jet的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 类型安全的 SQL 查询框架 - Jet 使用指南
Jet 是一个类型安全的 SQL 查询构建器,它通过代码生成方式为你的数据库结构创建类型安全的模型,避免了手写 SQL 字符串的繁琐和潜在错误。
Jet 核心特性
- 完全类型安全
- 支持多种数据库(PostgreSQL, MySQL, MariaDB, CockroachDB, SQLite)
- 自动代码生成
- 支持复杂查询(JOIN, 子查询等)
- 编译时检查 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)
性能考虑
- Jet 在编译时生成所有 SQL 构建代码,运行时开销极小
- 查询构建过程无反射使用
- 最终生成的 SQL 语句与手写 SQL 性能相同
总结
Jet 提供了类型安全的 SQL 查询构建方式,通过代码生成避免了手写 SQL 字符串的错误,同时保持了良好的性能。它特别适合需要严格类型检查的中大型项目,可以显著提高数据库操作的可靠性和开发效率。
对于简单的 CRUD 操作,Jet 可能显得有点重,但对于复杂查询和需要严格类型安全的场景,Jet 是一个非常优秀的选择。