Go 每日一库:sqlc 库,SQL 到 Go 代码的自动生成

Go 每日一库:sqlc 库,SQL 到 Go 代码的自动生成

【免费下载链接】go-daily-lib Go 每日一库 【免费下载链接】go-daily-lib 项目地址: https://gitcode.com/GitHub_Trending/go/go-daily-lib

你是否还在手动编写数据库操作代码?面对频繁的 SQL 变更,每次都要修改对应的 Go 结构体和方法?sqlc 库让这一切成为过去!通过 sqlc,你只需编写 SQL,工具会自动生成类型安全的 Go 代码,彻底告别手写 ORM 的繁琐。读完本文,你将掌握:

  • sqlc 的核心优势与工作流程
  • 从 SQL 定义到 Go 代码生成的完整步骤
  • 常见功能如查询、插入、删除的实现方式
  • 如何处理复杂查询和类型映射

sqlc 工作原理

sqlc 的核心思想是"SQL 优先",通过分析你的 SQL 语句自动生成对应的 Go 代码。其工作流程如下:

mermaid

这种方式确保了 SQL 与 Go 代码的一致性,同时保留了 SQL 的灵活性和性能优化能力。

快速开始

环境准备

首先需要安装 sqlc 工具:

go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest

项目结构

sqlc 推荐的项目结构如下:

sqlc/get-started/
├── main.go           # 应用代码
├── schema.sql        # 数据库表结构定义
├── query.sql         # SQL查询语句
├── sqlc.yaml         # sqlc配置文件
└── db/               # 自动生成的代码目录
    ├── db.go
    ├── models.go     # 数据模型
    └── query.sql.go  # 查询函数

定义数据库结构

创建 schema.sql 文件定义数据库表结构:

CREATE TABLE authors (
  id   BIGSERIAL PRIMARY KEY,
  name text      NOT NULL,
  bio  text
);

编写 SQL 查询

创建 query.sql 文件定义数据操作:

-- name: GetAuthor :one
SELECT * FROM authors
WHERE id = $1 LIMIT 1;

-- name: ListAuthors :many
SELECT * FROM authors
ORDER BY name;

-- name: CreateAuthor :one
INSERT INTO authors (
  name, bio
) VALUES (
  $1, $2
)
RETURNING *;

-- name: DeleteAuthor :exec
DELETE FROM authors
WHERE id = $1;

每个 SQL 语句都以 -- name: [函数名] :[命令类型] 格式的注释开头,sqlc 根据这些注释生成对应的 Go 函数。

配置 sqlc.yaml

创建 sqlc.yaml 配置文件:

version: "1"
packages:
  - name: "db"
    path: "./db"
    queries: "./query.sql"
    schema: "./schema.sql"

生成 Go 代码

运行以下命令生成 Go 代码:

sqlc generate

生成的代码位于 db 目录下,包括:

  • models.go: 定义与数据库表对应的结构体
  • query.sql.go: 包含所有 SQL 查询对应的 Go 函数
  • db.go: 数据库连接和事务相关代码

使用生成的代码

main.go 中使用生成的代码:

package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
    "golang.org/x/net/context"

    "github.com/darjun/go-daily-lib/sqlc/get-started/db"
)

func main() {
    // 连接数据库
    pq, err := sql.Open("postgres", "dbname=sqlc sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }

    // 创建查询对象
    queries := db.New(pq)

    // 查询所有作者
    authors, err := queries.ListAuthors(context.Background())
    if err != nil {
        log.Fatal("ListAuthors error:", err)
    }
    fmt.Println(authors)

    // 插入新作者
    insertedAuthor, err := queries.CreateAuthor(context.Background(), db.CreateAuthorParams{
        Name: "Brian Kernighan",
        Bio:  sql.NullString{String: "Co-author of The C Programming Language and The Go Programming Language", Valid: true},
    })
    if err != nil {
        log.Fatal("CreateAuthor error:", err)
    }
    fmt.Println(insertedAuthor)

    // 查询特定作者
    fetchedAuthor, err := queries.GetAuthor(context.Background(), insertedAuthor.ID)
    if err != nil {
        log.Fatal("GetAuthor error:", err)
    }
    fmt.Println(fetchedAuthor)

    // 删除作者
    err = queries.DeleteAuthor(context.Background(), insertedAuthor.ID)
    if err != nil {
        log.Fatal("DeleteAuthor error:", err)
    }
}

核心功能详解

数据模型

sqlc 会根据表结构自动生成对应的 Go 结构体,如 schema.sql 中的 authors 表会生成:

// models.go
type Author struct {
    ID   int64
    Name string
    Bio  sql.NullString
}

注意 bio 字段因为在表结构中允许为 NULL,所以使用了 sql.NullString 类型。

查询操作

对于 query.sql 中的每个 SQL 语句,sqlc 都会生成对应的 Go 函数。例如:

-- name: ListAuthors :many
SELECT * FROM authors ORDER BY name;

会生成:

func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) {
    // 实现代码
}

命令类型说明:

  • :one: 返回单行结果
  • :many: 返回多行结果
  • :exec: 执行命令(如 INSERT, UPDATE, DELETE),不返回结果
  • :execrows: 执行命令并返回受影响的行数
  • :returning: 执行命令并返回结果(如 PostgreSQL 的 RETURNING 子句)

参数绑定

sqlc 支持参数绑定,例如插入操作:

-- name: CreateAuthor :one
INSERT INTO authors (name, bio) VALUES ($1, $2) RETURNING *;

会生成带有参数结构体的函数:

type CreateAuthorParams struct {
    Name string
    Bio  sql.NullString
}

func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (Author, error) {
    // 实现代码
}

高级功能

自定义类型映射

sqlc.yaml 中可以配置自定义类型映射,例如:

version: "1"
packages:
  - name: "db"
    path: "./db"
    queries: "./query.sql"
    schema: "./schema.sql"
    types:
      - "sql:timestamptz -> go:time.Time"
      - "sql:uuid -> go:github.com/google/uuid.UUID"

事务支持

sqlc 生成的代码支持事务操作:

tx, err := db.BeginTx(ctx, nil)
if err != nil {
    // 处理错误
}
defer tx.Rollback()

q := db.New(tx)
// 在事务中执行操作
author, err := q.CreateAuthor(ctx, params)
if err != nil {
    // 处理错误
}

err = tx.Commit()
if err != nil {
    // 处理错误
}

实际应用示例

处理复杂查询

对于包含 JOIN 和聚合函数的复杂查询,sqlc 同样可以处理。例如:

-- name: GetAuthorWithBooks :one
SELECT a.*, COUNT(b.id) as book_count
FROM authors a
LEFT JOIN books b ON a.id = b.author_id
WHERE a.id = $1
GROUP BY a.id;

sqlc 会自动生成对应的结构体和函数:

type GetAuthorWithBooksRow struct {
    ID        int64
    Name      string
    Bio       sql.NullString
    BookCount int64
}

func (q *Queries) GetAuthorWithBooks(ctx context.Context, id int64) (GetAuthorWithBooksRow, error) {
    // 实现代码
}

批量操作

sqlc 支持批量插入操作:

-- name: BulkInsertAuthors :copyfrom
INSERT INTO authors (name, bio)
VALUES ($1, $2);

生成的函数支持批量插入:

func (q *Queries) BulkInsertAuthors(ctx context.Context, authors []BulkInsertAuthorsParams) (int64, error) {
    // 实现代码
}

总结

sqlc 提供了一种高效、类型安全的方式来处理 Go 应用中的数据库操作。通过 SQL 优先的方式,既保留了 SQL 的强大功能,又避免了手写 ORM 代码的繁琐和错误。

使用 sqlc 可以带来以下好处:

  • 类型安全:所有数据库操作都有类型检查
  • SQL 优化:可以直接编写优化的 SQL 语句
  • 减少重复工作:自动生成大量样板代码
  • 保持一致性:SQL 与 Go 代码自动同步

要了解更多高级功能,可以查看官方文档或项目中的示例代码:

下一步学习

  1. 尝试使用 sqlc 重构现有项目中的数据库操作代码
  2. 探索 sqlc 对不同数据库(如 MySQL、SQLite)的支持
  3. 学习如何编写更复杂的查询和类型映射
  4. 了解 sqlc 与迁移工具(如 golang-migrate)的结合使用

希望本文能帮助你快速掌握 sqlc 的使用,提升 Go 项目中数据库操作的开发效率和代码质量!

如果你觉得这篇文章有帮助,请点赞、收藏并关注,后续将带来更多 Go 每日一库的实用教程。

【免费下载链接】go-daily-lib Go 每日一库 【免费下载链接】go-daily-lib 项目地址: https://gitcode.com/GitHub_Trending/go/go-daily-lib

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值