golang实现惯用SQL构建与查询的插件库goqu的使用
Golang实现惯用SQL构建与查询的插件库goqu的使用
简介
goqu是一个表达式的SQL构建器和执行器,具有以下主要特性:
- 查询构建器
- 参数插值(如
SELECT * FROM "items" WHERE "id" = ?
->SELECT * FROM "items" WHERE "id" = 1
) - 支持多种SQL方言
- 支持插入、批量插入、更新和删除操作
- 支持将行扫描到结构体或原始值中
安装
如果使用Go模块:
go get -u github.com/doug-martin/goqu/v9
如果不使用Go模块(Go版本>1.10):
go get -u github.com/doug-martin/goqu
快速示例
查询(Select)
// 基本查询
sql, _, _ := goqu.From("test").ToSQL()
fmt.Println(sql)
// 输出: SELECT * FROM "test"
// 带条件的查询
sql, _, _ := goqu.From("test").Where(goqu.Ex{
"d": []string{"a", "b", "c"},
}).ToSQL()
fmt.Println(sql)
// 输出: SELECT * FROM "test" WHERE ("d" IN ('a', 'b', 'c'))
插入(Insert)
// 使用Cols和Vals插入多行
ds := goqu.Insert("user").
Cols("first_name", "last_name").
Vals(
goqu.Vals{"Greg", "Farley"},
goqu.Vals{"Jimmy", "Stewart"},
goqu.Vals{"Jeff", "Jeffers"},
)
insertSQL, args, _ := ds.ToSQL()
fmt.Println(insertSQL, args)
// 输出: INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
// 使用Record插入多行
ds := goqu.Insert("user").Rows(
goqu.Record{"first_name": "Greg", "last_name": "Farley"},
goqu.Record{"first_name": "Jimmy", "last_name": "Stewart"},
goqu.Record{"first_name": "Jeff", "last_name": "Jeffers"},
)
insertSQL, args, _ := ds.ToSQL()
fmt.Println(insertSQL, args)
// 输出: INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
// 使用结构体插入
type User struct {
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
}
ds := goqu.Insert("user").Rows(
User{FirstName: "Greg", LastName: "Farley"},
User{FirstName: "Jimmy", LastName: "Stewart"},
User{FirstName: "Jeff", LastName: "Jeffers"},
)
insertSQL, args, _ := ds.ToSQL()
fmt.Println(insertSQL, args)
// 输出: INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
更新(Update)
// 基本更新
sql, args, _ := goqu.Update("items").Set(
goqu.Record{"name": "Test", "address": "111 Test Addr"},
).ToSQL()
fmt.Println(sql, args)
// 输出: UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
// 使用结构体更新(带skipupdate标签)
type item struct {
Address string `db:"address"`
Name string `db:"name" goqu:"skipupdate"`
}
sql, args, _ := goqu.Update("items").Set(
item{Name: "Test", Address: "111 Test Addr"},
).ToSQL()
fmt.Println(sql, args)
// 输出: UPDATE "items" SET "address"='111 Test Addr' []
// 带条件的更新
sql, _, _ := goqu.Update("test").
Set(goqu.Record{"foo": "bar"}).
Where(goqu.Ex{
"a": goqu.Op{"gt": 10}
}).ToSQL()
fmt.Println(sql)
// 输出: UPDATE "test" SET "foo"='bar' WHERE ("a" > 10)
删除(Delete)
// 基本删除
ds := goqu.Delete("items")
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
// 输出: DELETE FROM "items" []
// 带条件的删除
sql, _, _ := goqu.Delete("test").Where(goqu.Ex{
"c": nil
}).ToSQL()
fmt.Println(sql)
// 输出: DELETE FROM "test" WHERE ("c" IS NULL)
为什么选择goqu?
goqu旨在实现以下目标:
- 使SQL生成变得简单愉快
- 创建一个表达式的DSL,可以在编译时发现SQL常见错误
- 提供一个DSL,考虑常见的SQL表达式,而不是每个数据库的每个细微差别
- 为开发人员提供以下能力:
- 在需要时使用SQL
- 轻松将结果扫描到原始值和结构体中
- 在需要时使用原生sql.Db方法
贡献
欢迎任何类型的贡献。如果您发现以下任何问题,请提出问题或创建PR:
- 文档问题
- 您发现文档在某些方面不足
如果遇到包的问题,请包括以下内容:
- 您使用的方言
- 问题描述
- 如何重现的简短示例(如果适用)
运行测试
测试套件需要PostgreSQL、MySQL和SQL Server数据库。您可以使用环境变量覆盖连接字符串:
go test -v -race ./...
您也可以使用docker-compose在容器中运行测试:
MYSQL_VERSION=8 POSTGRES_VERSION=13.4 SQLSERVER_VERSION=2017-CU8-ubuntu GO_VERSION=latest docker-compose run goqu
许可证
goqu在MIT许可证下发布。
更多关于golang实现惯用SQL构建与查询的插件库goqu的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang实现惯用SQL构建与查询的插件库goqu的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Goqu: Golang 惯用 SQL 构建与查询库
Goqu 是一个强大的 Golang SQL 构建器和查询库,它提供了流畅的 API 来构建类型安全的 SQL 查询。下面我将介绍 Goqu 的主要功能和使用方法。
安装
go get github.com/doug-martin/goqu/v9
基本使用
初始化连接
import (
"database/sql"
"github.com/doug-martin/goqu/v9"
_ "github.com/doug-martin/goqu/v9/dialect/mysql" // 根据数据库类型选择
)
func main() {
// 创建数据库连接
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err.Error())
}
defer db.Close()
// 创建 goqu 数据库实例
dialect := goqu.Dialect("mysql")
gq := dialect.DB(db)
}
查询构建
简单查询
sql, args, _ := goqu.From("users").
Select("id", "name", "email").
Where(goqu.Ex{
"status": "active",
"age": goqu.Op{"gt": 18},
}).
Order(goqu.I("created_at").Desc()).
Limit(10).
Offset(0).
ToSQL()
// 执行查询
rows, err := gq.Query(sql, args...)
复杂条件
sql, _, _ := goqu.From("users").
Select("*").
Where(goqu.ExOr{
"first_name": "John",
"last_name": "Doe",
}).
Where(goqu.C("age").Gt(18)).
Where(goqu.C("created_at").Between(
goqu.Range(goqu.L("2020-01-01"), goqu.L("2020-12-31")),
)).
ToSQL()
插入数据
// 单条插入
sql, args, _ := goqu.Insert("users").
Rows(goqu.Record{
"name": "John Doe",
"email": "john@example.com",
"age": 30,
}).
ToSQL()
// 批量插入
sql, args, _ := goqu.Insert("users").
Cols("name", "email", "age").
Vals(
goqu.Vals{"Jane Doe", "jane@example.com", 28},
goqu.Vals{"Bob Smith", "bob@example.com", 35},
).
ToSQL()
更新数据
sql, args, _ := goqu.Update("users").
Set(goqu.Record{"status": "inactive"}).
Where(goqu.Ex{"last_login": goqu.Op{"lt": "2020-01-01"}}).
ToSQL()
删除数据
sql, args, _ := goqu.Delete("users").
Where(goqu.Ex{"status": "banned"}).
ToSQL()
高级查询
连接查询
sql, _, _ := goqu.From("users").
Select("users.id", "users.name", "orders.amount").
Join(goqu.T("orders"), goqu.On(goqu.Ex{"users.id": goqu.I("orders.user_id")})).
Where(goqu.Ex{"users.status": "active"}).
ToSQL()
子查询
subQuery := goqu.From("orders").
Select("user_id", goqu.MAX("amount").As("max_amount")).
GroupBy("user_id")
sql, _, _ := goqu.From("users").
Select("users.*", "sub_query.max_amount").
Join(subQuery.As("sub_query"),
goqu.On(goqu.I("users.id").Eq(goqu.I("sub_query.user_id")))).
ToSQL()
函数和表达式
sql, _, _ := goqu.From("users").
Select(
goqu.COUNT("*").As("total"),
goqu.AVG("age").As("avg_age"),
goqu.MAX("created_at").As("last_created"),
).
ToSQL()
执行查询
// 查询单条记录
var user struct {
ID int64 `db:"id"`
Name string `db:"name"`
Email string `db:"email"`
}
found, err := gq.From("users").
Select("id", "name", "email").
Where(goqu.Ex{"id": 1}).
ScanStruct(&user)
// 查询多条记录
var users []struct {
ID int64 `db:"id"`
Name string `db:"name"`
Email string `db:"email"`
}
err := gq.From("users").
Select("id", "name", "email").
Where(goqu.Ex{"status": "active"}).
ScanStructs(&users)
事务支持
err := gq.WithTx(func(tx *goqu.TxDatabase) error {
// 在事务中执行操作
if _, err := tx.Insert("users").Rows(...).Executor().Exec(); err != nil {
return err
}
if _, err := tx.Update("accounts").Set(...).Executor().Exec(); err != nil {
return err
}
return nil
})
日志记录
import "github.com/doug-martin/goqu/v9/exp"
// 自定义日志记录器
gq.Logger(func(sql string, args []interface{}, err error) {
if err != nil {
log.Printf("Error: %v\nSQL: %s\nArgs: %v", err, sql, args)
} else {
log.Printf("SQL: %s\nArgs: %v", sql, args)
}
})
Goqu 提供了强大而灵活的 SQL 构建能力,同时保持了 Golang 的惯用风格。它支持多种数据库方言,包括 MySQL、PostgreSQL、SQLite 等,是替代原生 SQL 字符串拼接的优秀选择。