golang类型安全的SQL构建器和结构体映射插件库sq的使用

Golang类型安全的SQL构建器和结构体映射插件库sq的使用

sq是一个代码生成的、类型安全的查询构建器和Go结构体映射库。

主要特点

  1. 避免魔法字符串:sq从数据库生成表结构体,确保您编写的查询始终反映数据库中的实际内容。

  2. 更好的NULL处理:sq将NULL扫描为零值,同时仍提供检查列是否为NULL的能力。

  3. 映射函数即SELECT子句:您只需编写查询,执行它,如果没有错误,数据就已经在您的Go变量中。

安装

go get github.com/bokwoon95/go-structured-query

根据您使用的数据库类型,还需要安装对应的代码生成器:

# Postgres
go get github.com/bokwoon95/go-structured-query/cmd/sqgen-postgres

# MySQL
go get github.com/bokwoon95/go-structured-query/cmd/sqgen-mysql

生成表结构

从数据库生成表结构:

# Postgres
sqgen-postgres tables --database 'postgres://name:pass@localhost:5432/dbname?sslmode=disable' --overwrite

# MySQL
sqgen-mysql tables --database 'name:pass@tcp(127.0.0.1:3306)/dbname' --schemas dbname --overwrite

导入sq

根据您使用的SQL方言导入对应的sq包:

// Postgres
import (
    sq "github.com/bokwoon95/go-structured-query/postgres"
)

// MySQL
import (
    sq "github.com/bokwoon95/go-structured-query/mysql"
)

示例代码

SELECT查询

-- SQL
SELECT u.user_id, u.name, u.email, u.created_at
FROM public.users AS u
WHERE u.name = 'Bob';
// Go
u := tables.USERS().As("u") // table is code generated
var user User
var users []User
err := sq.
    From(u).
    Where(u.NAME.EqString("Bob")).
    Selectx(func(row *sq.Row) {
        user.UserID = row.Int(u.USER_ID)
        user.Name = row.String(u.NAME)
        user.Email = row.String(u.EMAIL)
        user.CreatedAt = row.Time(u.CREATED_AT)
    }, func() {
        users = append(users, user)
    }).
    Fetch(db)
if err != nil {
    // handle error
}

INSERT操作

-- SQL
INSERT INTO public.users (name, email)
VALUES ('Bob', 'bob@email.com'), ('Alice', 'alice@email.com'), ('Eve', 'eve@email.com');
// Go
u := tables.USERS().As("u") // table is code generated
users := []User{
    {Name: "Bob",   Email: "bob@email.com"},
    {Name: "Alice", Email: "alice@email.com"},
    {Name: "Eve  ", Email: "eve@email.com"},
}
rowsAffected, err := sq.
    InsertInto(u).
    Valuesx(func(col *sq.Column) {
        for _, user := range users {
            col.SetString(u.NAME, user.Name)
            col.SetString(u.EMAIL, user.Email)
        }
    }).
    Exec(db, sq.ErowsAffected)
if err != nil {
    // handle error
}

UPDATE操作

-- SQL
UPDATE public.users
SET name = 'Bob', password = 'qwertyuiop'
WHERE email = 'bob@email.com';
// Go
u := tables.USERS().As("u") // table is code generated
user := User{
    Name:     "Bob",
    Email:    "bob@email.com",
    Password: "qwertyuiop",
}
rowsAffected, err := sq.
    Update(u).
    Setx(func(col *sq.Column) {
        col.SetString(u.NAME, user.Name)
        col.SetString(u.PASSWORD, user.Password)
    }).
    Where(u.EMAIL.EqString(user.Email)).
    Exec(db, sq.ErowsAffected)
if err != nil {
    // handle error
}

DELETE操作

-- SQL
DELETE FROM public.users AS u
USING public.user_roles AS ur
JOIN public.user_roles_students AS urs ON urs.user_role_id = ur.user_role_id
WHERE u.user_id = ur.user_id AND urs.team_id = 15;
// Go
u   := tables.USERS().As("u")                 // tables are code generated
ur  := tables.USER_ROLES().As("ur")           // tables are code generated
urs := tables.USER_ROLES_STUDENTS().As("urs") // tables are code generated
rowsAffected, err := sq.
    DeleteFrom(u).
    Using(ur).
    Join(urs, urs.USER_ROLE_ID.Eq(ur.USER_ROLE_ID)).
    Where(
        u.USER_ID.Eq(ur.USER_ID),
        urs.TEAM_ID.EqInt(15),
    ).
    Exec(db, sq.ErowsAffected)
if err != nil {
    // handle error
}

项目状态

外部API被认为是稳定的。任何更改只会添加到API中(如支持自定义记录器和结构化日志记录)。


更多关于golang类型安全的SQL构建器和结构体映射插件库sq的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang类型安全的SQL构建器和结构体映射插件库sq的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 类型安全的 SQL 构建器和结构体映射:sq 库使用指南

sq 是一个 Go 语言的类型安全 SQL 构建器和结构体映射库,它提供了编译时类型检查的 SQL 查询构建能力,同时支持将查询结果自动映射到结构体。

安装 sq

go get github.com/bokwoon95/sq

基本用法

1. 构建 SQL 查询

import (
    "github.com/bokwoon95/sq"
    "github.com/bokwoon95/sq/sqlite"
)

type User struct {
    ID    int
    Name  string
    Email string
}

func main() {
    // 创建查询构建器
    query := sqlite.From("users").
        Select("id", "name", "email").
        Where("age > ?", 18).
        OrderBy("name ASC")
    
    // 获取生成的 SQL 和参数
    sql, args, err := sq.ToSQL(query)
    if err != nil {
        panic(err)
    }
    
    fmt.Println("SQL:", sql)
    fmt.Println("Args:", args)
}

2. 结构体映射

sq 支持将查询结果自动映射到结构体:

func getUsers(db *sql.DB) ([]User, error) {
    var users []User
    
    // 构建查询
    query := sqlite.From("users").
        Select("id", "name", "email").
        Where("active = ?", true)
    
    // 执行查询并映射结果
    err := sq.FetchAll(db, query, &users)
    if err != nil {
        return nil, err
    }
    
    return users, nil
}

3. 类型安全的查询构建

sq 支持类型安全的查询构建,可以避免字段名拼写错误:

// 定义表结构
type USER struct {
    sq.TableStruct
    ID    sq.NumberField
    Name  sq.StringField
    Email sq.StringField
    Age   sq.NumberField
}

func getAdultUsers(db *sql.DB) ([]User, error) {
    u := USER{TableStruct: sq.TableStruct{TableName: "users"}}
    var users []User
    
    query := sqlite.From(u).
        Select(u.ID, u.Name, u.Email).
        Where(u.Age.Gt(18)).
        OrderBy(u.Name)
    
    err := sq.FetchAll(db, query, &users)
    if err != nil {
        return nil, err
    }
    
    return users, nil
}

高级功能

1. 复杂查询

func getUsersWithPosts(db *sql.DB) ([]struct {
    User  User
    Posts []Post
}, error) {
    u := USER{TableStruct: sq.TableStruct{TableName: "users"}}
    p := POST{TableStruct: sq.TableStruct{TableName: "posts"}}
    
    var result []struct {
        User  User
        Posts []Post
    }
    
    query := sqlite.From(u).
        LeftJoin(p, p.UserID.Eq(u.ID)).
        Select(
            u.ID, u.Name, u.Email,
            p.ID, p.Title, p.Content,
        ).
        Where(u.Active.IsTrue())
    
    err := sq.FetchAll(db, query, &result)
    if err != nil {
        return nil, err
    }
    
    return result, nil
}

2. 事务支持

func createUserWithTransaction(db *sql.DB, user User) error {
    tx, err := db.Begin()
    if err != nil {
        return err
    }
    defer tx.Rollback()
    
    u := USER{TableStruct: sq.TableStruct{TableName: "users"}}
    
    // 插入用户
    _, err = sq.Exec(tx, sqlite.InsertInto(u).
        Columns(u.Name, u.Email).
        Values(user.Name, user.Email))
    if err != nil {
        return err
    }
    
    return tx.Commit()
}

3. 条件构建

func searchUsers(db *sql.DB, nameFilter string, minAge int) ([]User, error) {
    u := USER{TableStruct: sq.TableStruct{TableName: "users"}}
    var users []User
    
    query := sqlite.From(u).
        Select(u.ID, u.Name, u.Email).
        Where(u.Age.Gte(minAge))
    
    if nameFilter != "" {
        query = query.Where(u.Name.Like("%" + nameFilter + "%"))
    }
    
    err := sq.FetchAll(db, query, &users)
    if err != nil {
        return nil, err
    }
    
    return users, nil
}

优势

  1. 类型安全:编译时检查 SQL 字段和类型
  2. 自动映射:查询结果自动映射到结构体
  3. SQL 注入防护:自动参数化查询
  4. 多数据库支持:支持 SQLite, PostgreSQL, MySQL 等
  5. 链式调用:流畅的 API 设计

sq 库通过类型安全的方式构建 SQL 查询,大大减少了运行时错误的风险,同时保持了代码的简洁性和可读性。

回到顶部