golang轻量级易学习的SQL查询构建器插件库bqb的使用
Golang轻量级易学习的SQL查询构建器插件库bqb的使用
基本介绍
bqb是一个简单、轻量级且快速的Golang SQL查询构建器库。它具有以下特点:
- 简单、轻量级且快速
- 支持任何SQL语法
- 不需要学习特殊语法或操作符
- 100%测试覆盖率
兼容性
bqb已经测试过与sqlite、PostgreSQL和MySQL兼容,可以使用database/sql
、pq
、pgx
和sqlx
等驱动。它应该与任何使用?
或$
参数语法的数据库接口和数据库完全兼容。
注意:Go v1.20+
是BQB >= v1.4.0
的要求。Go v1.17+
是BQB <= v1.3.0
的要求。
基本用法
简单查询
q := bqb.New("SELECT * FROM places WHERE id = ?", 1234)
sql, params, err := q.ToSql()
生成结果:
SELECT * FROM places WHERE id = ?
参数:
PARAMS: [1234]
转义问号
在Postgres查询中使用双问号??
来转义?
。例如:
q := bqb.New("SELECT * FROM places WHERE json_obj_column ?? 'key'")
sql, params, err := q.ToPgsql()
生成结果:
SELECT * FROM places WHERE json_obj_column ? 'key'
参数:
PARAMS: []
PostgreSQL支持
只需调用ToPgsql()
方法而不是ToSql()
来将查询转换为Postgres语法:
q := bqb.New("DELETE FROM users").
Space("WHERE id = ? OR name IN (?)", 7, []string{"delete", "remove"}).
Space("LIMIT ?", 5)
sql, params, err := q.ToPgsql()
生成结果:
DELETE FROM users WHERE id = $1 OR name IN ($2, $3) LIMIT $4
参数:
PARAMS: [7, "delete", "remove", 5]
原始查询
ToRaw()
调用返回一个填充了值而不是参数化的字符串。
q := New("a = ?, b = ?, c = ?", "my a", 1234, nil)
sql, err := q.ToRaw()
生成结果:
a = 'my a', b = 1234, c = NULL
类型支持
q := bqb.New(
"int:? string:? []int:? []string:? Query:? JsonMap:? nil:? []intf:?",
1, "2", []int{3, 3}, []string{"4", "4"}, bqb.New("5"), bqb.JsonMap{"6": 6}, nil, []interface{}{"a",1,true},
)
sql, _ := q.ToRaw()
生成结果:
int:1 string:'2' []int:3,3 []string:'4','4' Query:5 JsonMap:'{"6":6}' nil:NULL []intf:'a',1,true
IN查询
[]string
、[]*string
、[]int
、[]*int
和[]any
/[]interface{}
类型的参数会自动展开。
q := bqb.New(
"strs:(?) *strs:(?) ints:(?) *ints:(?) intfs:(?)",
[]string{"a", "b"}, []*string{}, []int{1, 2}, []*int{}, []any{3, true},
)
sql, params, _ := q.ToSql()
生成结果:
SQL: strs:(?,?) *strs:(?) ints:(?,?) *ints:(?) intfs:(?,?)
PARAMS: [a b <nil> 1 2 <nil> 3 true]
JSON参数
有两个辅助结构体JsonMap
和JsonList
使JSON转换更简单:
sql, err := bqb.New(
"INSERT INTO my_table (json_map, json_list) VALUES (?, ?)",
bqb.JsonMap{"a": 1, "b": []string{"a","b","c"}},
bqb.JsonList{"string",1,true,nil},
).ToRaw()
生成结果:
INSERT INTO my_table (json_map, json_list)
VALUES ('{"a": 1, "b": ["a","b","c"]}', '["string",1,true,null]')
查询构建
基本示例
sel := bqb.New("SELECT")
// later
sel.Space("id")
// even later
sel.Comma("age").Comma("email")
生成结果:
SELECT id,age,email
高级示例
sel := bqb.Optional("SELECT")
if getName {
sel.Comma("name")
}
if getId {
sel.Comma("id")
}
if !getName && !getId {
sel.Comma("*")
}
from := bqb.New("FROM my_table")
where := bqb.Optional("WHERE")
if filterAdult {
adultCond := bqb.New("name = ?", "adult")
if ageCheck {
adultCond.And("age > ?", 20)
}
where.And("(?)", adultCond)
}
if filterChild {
where.Or("(name = ? AND age < ?)", "youth", 21)
}
q := bqb.New("? ? ?", sel, from, where).Space("LIMIT ?", 10)
假设所有值都为true,查询将如下所示:
SELECT name,id FROM my_table WHERE (name = 'adult' AND age > 20) OR (name = 'youth' AND age < 21) LIMIT 10
如果getName
和getId
为false,查询将是:
SELECT * FROM my_table WHERE (name = 'adult' AND age > 20) OR (name = 'youth' AND age < 21) LIMIT 10
如果所有值都为false,查询将是:
SELECT * FROM my_table LIMIT 10
方法
所有查询修改方法都采用一个字符串(查询文本)和可变长度接口(查询参数)。例如q.And("abc")
将在查询中添加AND abc
。
q := bqb.Optional("WHERE")
q.Empty() // returns true
q.Len() // returns 0
q.Space("1 = 2") // query is now WHERE 1 = 2
q.Empty() // returns false
q.Len() // returns 1
q.And("b") // query is now WHERE 1 = 2 AND b
q.Or("c") // query is now WHERE 1 = 2 AND b OR c
q.Concat("d") // query is now WHERE 1 = 2 AND b OR cd
q.Comma("e") // query is now WHERE 1 = 2 AND b OR cd,e
q.Join("+", "f") // query is now WHERE 1 = 2 AND b OR cd,e+f
有效的args
包括string
、int
、floatN
、*Query
、[]int
、Embedder
、Embedded
、driver.Valuer
或[]string
。
常见问题
为什么使用bqb而不是字符串构建器?
bqb提供了几个优于字符串构建器的好处:
- 轻松适应MySQL或PostgreSQL而无需更改参数
- 如果
filterBobs
和filterAge
都为false,则隐藏"WHERE"子句 - 更简洁的代码
为什么不使用完整的查询构建器?
bqb提供了更接近原生SQL的体验,同时保持了构建动态查询的灵活性。它不需要学习特殊的语法或操作符,使得代码更易于理解和维护。
更多关于golang轻量级易学习的SQL查询构建器插件库bqb的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang轻量级易学习的SQL查询构建器插件库bqb的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang轻量级SQL查询构建器bqb使用指南
bqb是一个轻量级、易学习的Golang SQL查询构建器库,它提供了简单直观的方式来构建SQL查询,同时避免了手写SQL字符串的繁琐和潜在错误。
安装bqb
go get github.com/nullism/bqb
基本用法
1. 简单查询构建
package main
import (
"fmt"
"github.com/nullism/bqb"
)
func main() {
query, args := bqb.NewQuery(
bqb.Select("id", "name", "email"),
bqb.From("users"),
bqb.Where("age", ">", 18),
bqb.OrderBy("name", "ASC"),
bqb.Limit(10),
).ToSql()
fmt.Println("SQL:", query)
fmt.Println("Args:", args)
}
输出结果:
SQL: SELECT id, name, email FROM users WHERE age > ? ORDER BY name ASC LIMIT ?
Args: [18 10]
2. 复杂查询示例
// 复杂查询构建
query, args := bqb.NewQuery(
bqb.Select("u.id", "u.name", "COUNT(o.id) as order_count"),
bqb.From("users u"),
bqb.LeftJoin("orders o", "u.id = o.user_id"),
bqb.Where("u.status", "=", "active"),
bqb.GroupBy("u.id", "u.name"),
bqb.Having("COUNT(o.id)", ">", 5),
bqb.OrderBy("order_count", "DESC"),
bqb.Limit(20),
).ToSql()
3. 插入数据
// 插入单条数据
query, args := bqb.NewQuery(
bqb.Insert("users"),
bqb.Pairs{
"name": "John Doe",
"email": "john@example.com",
"age": 30,
},
).ToSql()
// 批量插入
users := []map[string]interface{}{
{"name": "Alice", "email": "alice@example.com", "age": 25},
{"name": "Bob", "email": "bob@example.com", "age": 28},
}
query, args := bqb.NewQuery(
bqb.Insert("users"),
bqb.BatchValues(users),
).ToSql()
4. 更新数据
query, args := bqb.NewQuery(
bqb.Update("users"),
bqb.Set("name", "John Smith"),
bqb.Set("email", "john.smith@example.com"),
bqb.Where("id", "=", 123),
).ToSql()
5. 删除数据
query, args := bqb.NewQuery(
bqb.Delete(),
bqb.From("users"),
bqb.Where("last_login", "<", "2023-01-01"),
).ToSql()
高级特性
1. 条件构建
// 复杂条件
query, args := bqb.NewQuery(
bqb.Select("*"),
bqb.From("products"),
bqb.Where(
bqb.Or(
bqb.And("price", ">", 100, "stock", ">", 0),
bqb.And("discount", "=", true, "rating", ">=", 4),
),
),
).ToSql()
2. 子查询
// 使用子查询
subQuery := bqb.NewQuery(
bqb.Select("user_id"),
bqb.From("orders"),
bqb.GroupBy("user_id"),
bqb.Having("SUM(amount)", ">", 1000),
)
query, args := bqb.NewQuery(
bqb.Select("*"),
bqb.From("users"),
bqb.Where("id", "IN", subQuery),
).ToSql()
3. 原生SQL片段
// 混合原生SQL
query, args := bqb.NewQuery(
bqb.Select("id", "name", bqb.Raw("CONCAT(first_name, ' ', last_name) AS full_name")),
bqb.From("users"),
bqb.Where(bqb.Raw("created_at > NOW() - INTERVAL '7 days'")),
).ToSql()
事务支持
// 事务示例
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
// 构建更新查询
updateQuery := bqb.NewQuery(
bqb.Update("accounts"),
bqb.Set("balance", bqb.Raw("balance - ?", 100)),
bqb.Where("id", "=", 1),
)
// 执行更新
if _, err := tx.Exec(updateQuery.ToSql()); err != nil {
tx.Rollback()
log.Fatal(err)
}
// 构建插入查询
insertQuery := bqb.NewQuery(
bqb.Insert("transactions"),
bqb.Pairs{
"account_id": 1,
"amount": -100,
"type": "withdrawal",
},
)
// 执行插入
if _, err := tx.Exec(insertQuery.ToSql()); err != nil {
tx.Rollback()
log.Fatal(err)
}
tx.Commit()
优势总结
- 简单易学:API设计直观,学习曲线平缓
- 类型安全:减少SQL注入风险
- 轻量级:不依赖ORM,只专注于查询构建
- 可组合:查询部分可以独立构建再组合
- 可扩展:支持原生SQL片段满足特殊需求
bqb非常适合需要灵活构建SQL查询但又不想引入完整ORM的项目,它提供了足够的表达能力来处理大多数SQL场景,同时保持了代码的简洁和可维护性。