golang轻量级易学习的SQL查询构建器插件库bqb的使用

Golang轻量级易学习的SQL查询构建器插件库bqb的使用

基本介绍

bqb是一个简单、轻量级且快速的Golang SQL查询构建器库。它具有以下特点:

  1. 简单、轻量级且快速
  2. 支持任何SQL语法
  3. 不需要学习特殊语法或操作符
  4. 100%测试覆盖率

兼容性

bqb已经测试过与sqlite、PostgreSQL和MySQL兼容,可以使用database/sqlpqpgxsqlx等驱动。它应该与任何使用?$参数语法的数据库接口和数据库完全兼容。

注意: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参数

有两个辅助结构体JsonMapJsonList使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

如果getNamegetId为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包括stringintfloatN*Query[]intEmbedderEmbeddeddriver.Valuer[]string

常见问题

为什么使用bqb而不是字符串构建器?

bqb提供了几个优于字符串构建器的好处:

  1. 轻松适应MySQL或PostgreSQL而无需更改参数
  2. 如果filterBobsfilterAge都为false,则隐藏"WHERE"子句
  3. 更简洁的代码

为什么不使用完整的查询构建器?

bqb提供了更接近原生SQL的体验,同时保持了构建动态查询的灵活性。它不需要学习特殊的语法或操作符,使得代码更易于理解和维护。


更多关于golang轻量级易学习的SQL查询构建器插件库bqb的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于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()

优势总结

  1. 简单易学:API设计直观,学习曲线平缓
  2. 类型安全:减少SQL注入风险
  3. 轻量级:不依赖ORM,只专注于查询构建
  4. 可组合:查询部分可以独立构建再组合
  5. 可扩展:支持原生SQL片段满足特殊需求

bqb非常适合需要灵活构建SQL查询但又不想引入完整ORM的项目,它提供了足够的表达能力来处理大多数SQL场景,同时保持了代码的简洁和可维护性。

回到顶部