sqlc 和 goose 最佳实践
最近有使用 Golang 来写一些小的项目,需要使用到数据库,之前有使用过 GORM 这种 ORM 框架,开发起来非常方便,但是发现项目的代码多了之后管理起来不是非常方便,所以学习了一下 goose 和 sqlc 这两个工具,这篇文章就是总结一下两个工具如何搭配使用。
goose 数据库版本迁移工具
安装
goose 是 Go 语言写的一个数据库迁移工具,能够以版本控制的方式帮我们管理数据库当中的表。
在使用 goose 之前需要使用下面的命令进行安装。
zsh
go install github.com/pressly/goose/v3/cmd/goose@latest
出现如下命令说明成功安装了 goose 。

使用
1. 配置环境变量
在使用 goose 的时候默认会读取环境变量当中的 GOOSE_DRIVER 、 GOOSE_DBSTRING 、GOOSE_MIGRATION_DIR 三个环境变量。
GOOSE_DRIVER: goose 在执行数据库迁移的时候使用的数据库驱动类型GOOSE_DBSTRING: goose 在执行数据库迁移的时候使用的数据库链接 URLGOOSE_MIGRATION_DIR:goose 读取的迁移文件目录
当然,goose 不止有上面三个环境变量的配置,更多的可以查看官方介绍文档。
pressly.github.io/goose/docum...
也可以像下面这样,创建一个 .env 文件,然后写入相关的内容,在使用 goose 的时候会默认载入 .env 当中的环境变量配置。
env
GOOSE_DRIVER=postgres
GOOSE_DBSTRING=postgres://postgres:123456@localhost:5432/goose_test
GOOSE_MIGRATION_DIR=./sql/schema
2. 创建 schema
执行下面的命令来创建一个迁移的 sql 文件
zsh
goose create create_user_table sql
如下图所示,可以看到在项目的 sql/schema 目录当中创建一个 sql 文件。

在 sql 文件当中写入如下的内容。
sql
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS account
(
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS account;
-- +goose StatementEnd
文件当中的 -- +goose Up 下面的部分是在执行 goose up 让数据库的版本 +1 的时候生效;-- +goose Down 下面的部分对应的上面部分的反向操作。上面的 sql goose up 的部分就是在数据库当中创建一张 account 表,而 goose down 的部分就是把数据当中的 account 表删掉。
3. 执行迁移
有了数据库迁移文件之后就可以开始执行数据库迁移了。常用的命令有下面两个:
goose up: 这个命令能够把当前数据库的版本升级到最新版本,也就是你最新创建的那个数据库迁移文件的版本。goose down:这个命令会将当前的数据库版本进行降低,每次执行都降低一个版本。
更多的详细命令可以直接使用 goose 命令来查看详情。
下图使用一次 goose up 把最新的两次 sql 迁移文件都应用到数据当中,随后使用两次 goose down 把数据库的版本恢复到最开始版本。

20251125133155_create_post_table.sql 的内容如下:
sql
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS post
(
id SERIAL PRIMARY KEY,
title text NOT NULL,
body text
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS post;
-- +goose StatementEnd
sqlc
sqlc 是一个为 Go 语言设计的 SQL 编译器,它能够从 SQL 查询生成完全类型安全的惯用 Go 代码。它不是一个 ORM,而是一个代码生成工具,专门解决数据库交互中的类型安全问题。
安装
sqlc 本质是一个可执行程序,使用下面的命令来安装。
zsh
go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
配置
安装好了之后,就能够直接在命令行使用 sqlc 的命令。在项目当中使用我们需要先在项目的根目录下创建一个 sqlc.yaml 或者 sqlc.yml 的配置文件。sqlc 在生成代码的时候会自动读取配置文件当中的配置。
这里我使用的是 postgresql , 也可以使用 sqlc init 来自动创建配置文件,我这里的配置文件如下所示。
yaml
version: "2"
sql:
- engine: "postgresql"
queries: "sql/queries"
schema: "sql/schema"
gen:
go:
package: "db"
out: "db"
sql_package: "pgx/v5"
配置文件当中的 queries 配置了我需要使用的 SQL 语句所在的目录,schema 配置了数据库表结构,可以直接使用 goose 的 schema 。
gen 相关的配置就是配置生成的代码的一些信息,package 指定生成代码的包名,out 指定生成代码的文件夹, sql_package 指定生成的代码文件使用的数据库包。
编写 SQL
配置好了之后,就可以开始写自己的业务代码需要用到的 SQL 了,和配置文件指定的一样,把业务需要用到的 SQL 文件都写到 sql/queries 里面,创建一个 user.sql。
这里我直接给出一个增、删、改、查的示例。
sql
-- name: GetAccountById :one
SELECT *
FROM account
WHERE id = $1 LIMIT 1;
-- name: CreateUser :one
INSERT INTO account (name, email, password)
VALUES ($1, $2, $3) RETURNING *;
-- name: UpdateUser :exec
UPDATE account
SET name = $1, email = $2, password = $3
WHERE id = $4;
-- name: DeleteUser :exec
DELETE FROM account
WHERE id = $1;
每个 SQL 开始之前的注释当中的 name 代表生成的代码当中的方法名,后面的 :one 代表返回一条记录,也可以是多条 :many ,也可以什么也不返回 :exec 。具体的使用可以参照详细的官方文档。
生成代码
接着就是使用 sqlc 来帮我们生成和数据库交互的相关代码。
直接在项目的根目录下执行 sqlc generate 即可,如下图所示,成功的在项目下的 db 文件夹生成了一些代码。

接下来就愉快的在自己的项目当中使用吧!这里我贴出这个调用上面的 user.sql 的调用示例。
go
package main
import (
"context"
"fmt"
"github.com/dimplesY/goose_test/db" "github.com/jackc/pgx/v5")
func main() {
conn, _ := pgx.Connect(context.Background(), "postgres://postgres:123456@localhost:5432/goose_test")
defer conn.Close(context.Background())
queries := db.New(conn)
user, _ := queries.CreateUser(context.Background(), db.CreateUserParams{
Name: "1",
Email: "1",
Password: "1",
})
fmt.Println(user)
u1, _ := queries.GetAccountById(context.Background(), user.ID)
fmt.Println(u1)
_ = queries.UpdateUser(context.Background(), db.UpdateUserParams{
Name: "2",
Email: "2",
Password: "2",
ID: user.ID,
})
u2, _ := queries.GetAccountById(context.Background(), user.ID)
fmt.Println(u2)
_ = queries.DeleteUser(context.Background(), user.ID)
u3, _ := queries.GetAccountById(context.Background(), user.ID)
fmt.Println(u3)
}
运行结构如下图所示:
