Go 每日一库:sqlc 库,SQL 到 Go 代码的自动生成
【免费下载链接】go-daily-lib Go 每日一库 项目地址: https://gitcode.com/GitHub_Trending/go/go-daily-lib
你是否还在手动编写数据库操作代码?面对频繁的 SQL 变更,每次都要修改对应的 Go 结构体和方法?sqlc 库让这一切成为过去!通过 sqlc,你只需编写 SQL,工具会自动生成类型安全的 Go 代码,彻底告别手写 ORM 的繁琐。读完本文,你将掌握:
- sqlc 的核心优势与工作流程
- 从 SQL 定义到 Go 代码生成的完整步骤
- 常见功能如查询、插入、删除的实现方式
- 如何处理复杂查询和类型映射
sqlc 工作原理
sqlc 的核心思想是"SQL 优先",通过分析你的 SQL 语句自动生成对应的 Go 代码。其工作流程如下:
这种方式确保了 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 代码自动同步
要了解更多高级功能,可以查看官方文档或项目中的示例代码:
下一步学习
- 尝试使用 sqlc 重构现有项目中的数据库操作代码
- 探索 sqlc 对不同数据库(如 MySQL、SQLite)的支持
- 学习如何编写更复杂的查询和类型映射
- 了解 sqlc 与迁移工具(如 golang-migrate)的结合使用
希望本文能帮助你快速掌握 sqlc 的使用,提升 Go 项目中数据库操作的开发效率和代码质量!
如果你觉得这篇文章有帮助,请点赞、收藏并关注,后续将带来更多 Go 每日一库的实用教程。
【免费下载链接】go-daily-lib Go 每日一库 项目地址: https://gitcode.com/GitHub_Trending/go/go-daily-lib
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



