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旨在实现以下目标:

  1. 使SQL生成变得简单愉快
  2. 创建一个表达式的DSL,可以在编译时发现SQL常见错误
  3. 提供一个DSL,考虑常见的SQL表达式,而不是每个数据库的每个细微差别
  4. 为开发人员提供以下能力:
    • 在需要时使用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 字符串拼接的优秀选择。

回到顶部