本节将学习使用Golang
来做CRUD
操作。
安装后,可以使用sqlc version
,查看安装的版本;sqlc help
,查看命令帮助。
compile
编译命令,用于检查SQL
语法和类型错误generate
最重要的命令,生成
,它将为我们检查语法错误,并从SQL
语句中生成Golang
代码init
用来创建一个空的 sqlc.yaml
配置文件
在 vscode
中,就可以看到它创建了一个 sqlc.yaml
文件
打开文档,https://docs.sqlc.dev/en/latest/tutorials/getting-started-postgresql.html,可以看到:
我们复制它,替换自动生成的sqlc.yaml
文件内容。
其中,
name
表示,将生成的Go
包名字是什么,把它改成db
path
指定存放生成的 Golang
代码的目录,在我们的项目db
目录下,新建子目录sqlc
,这里的path
,修改为 ./db/sqlc
queries
指定在哪里查找 SQL
查询语句,在我们的项目db
目录下,新建子目录query
,这里的queries
, 修改为 ./db/query/
schema
,包含数据库迁移文件的目录,这里,我们改成 ./db/migration/
engine
表示我们使用什么数据库,这里是 postgresql
, 不去动它。- 另外,增加
emit_json_tags
,设置为 true
, 将 JSON
标记添加到生成的结构体中。
改好的配置文件内容如下:
version: 1
packages:
- path: "./db/sqlc"
name: "db"
engine: "postgresql"
schema: "./db/migration/"
queries: "./db/query/"
emit_json_tags: true
目录结构如下:
打开终端,执行
sqlc generate
会发现如下错误:
error parsing queries: no queries contained in paths /goproject/simplebank/db/query
因为,query
目录里还没有查询语句文件,稍后我们来写。
现在,先在Makefile
文件里添加一个新的命令 sqlc
, 它将帮助我们的团队成员,在一个地方找到所有用于开发的命令。改完如下:
postgres:
docker run --name postgres14 -e POSTGRES_PASSWORD=123456 -e POSTGRES_USER=root -p 5432:5432 -d postgres:14-alpine
createdb:
docker exec -it postgres14 createdb --username=root --owner=root simple_bank
dropdb:
docker exec -it postgres14 dropdb simple_bank
migrateup:
migrate --path db/migration --database="postgresql://root:123456@localhost:5432/simple_bank?sslmode=disable" -verbose up
migratedown:
migrate --path db/migration --database="postgresql://root:123456@localhost:5432/simple_bank?sslmode=disable" -verbose down
sqlc:
sqlc generate
.PHONY: postgres, createdb, dropdb, migrateup, migratedown, sqlc
接下来,编写第一个SQL
语句来创建一个account
,在项目的db/query
目录下,新建一个account.sql
文件,在 SQLC
的文档中找到这段,复制到 account.sql
文件中
这是一条基础的INSERT
SQL语句,需要注意的是上面的注释-- name: CreateAuthor :one
,该注释将会让 SQLC
如何为此SQL语句生成 Golang
的函数名称,这里我们改成CreateAccount
,one
表示返回1个Account
对象。改完如下:
INSERT INTO accounts (
owner,
balance,
currency
) VALUES (
$1, $2, $3
)
RETURNING *;
最后 RETURNING *
表示创建Account
后,返回所有字段的内容。
然后,我们在终端执行:
make sqlc
可以看到它执行成功了,没有错误,这时,可以在项目的db/sqlc
目录里生成好了3个文件,account.sql.go
、db.go
和models.go
。
- 可以看到
models.go
里面的3个结构体,映射我们的数据表,JSON
标签也有了,注释也有了,注释用的是之前我们建表时里面的注释,并且结构体的命名自动把复数变成了单数。 db.go
里面定义了DB操作的接口方法。account.sql.go
其中的 package
名称db
,是之前我们配置指定的,其中的createAccount
把之前写的RETURNING *
变成了 RETURNING id, owner, balance, currency, created_at
,这样可以让查询语句更清晰。
CreateAccountParams
结构体有了我们在创建新账户时需要的所有字段。
type CreateAccountParams struct {
Owner string `json:"owner"`
Balance int64 `json:"balance"`
Currency string `json:"currency"`
}
CreateAccount
方法定义了一个Queries
为接收者,返回Account
或者错误,主要参数是CreateAccountParams
func (q *Queries) CreateAccount(ctx context.Context, arg CreateAccountParams) (Account, error) {
row := q.db.QueryRowContext(ctx, createAccount, arg.Owner, arg.Balance, arg.Currency)
var i Account
err := row.Scan(
&i.ID,
&i.Owner,
&i.Balance,
&i.Currency,
&i.CreatedAt,
)
return i, err
}
这时,我们看到代码里面有红色下划线报错:
是因为,我们还没有为项目初始化模块,在项目下打开终端,执行
go mod init simplebank
再看account.sql.go
,所有的报错提示已经没有了。
可以看到,生成的代码,最终是使用database/sql
,而不需要我们手动拼接这些参数,赞。而且,它会在生成代码之前检查SQL
语句的语法,以避免写SQL
时出现低级错误。
特别注意,我们不要手动修改SQLC
生成的go
文件内容,因为,我们每次运行make sqlc
时,这些文件都会重新生成,如果我们在这里修改了内容,它会被重新覆盖掉。
https://docs.sqlc.dev/en/latest/howto/update.html,可以看到:
把它复制到我们的account.sql
文件中,并做修改,如下:
INSERT INTO accounts (
owner,
balance,
currency
) VALUES (
$1, $2, $3
)
RETURNING *;
SELECT * FROM accounts
WHERE id = $1 LIMIT 1;
SELECT * FROM accounts
ORDER BY id
LIMIT $1
OFFSET $2;
UPDATE accounts SET balance = $2 WHERE id = $1;
再次运行make sqlc
,之后account.sql.go
又多了个UpdateAccount
func (q *Queries) UpdateAccount(ctx context.Context, arg UpdateAccountParams) error {
_, err := q.db.ExecContext(ctx, updateAccount, arg.ID, arg.Balance)
return err
}
有时候,我们需要得到更新后的结果,就需要把更新后的结果返回出来,这里再修改一下SQL
语句,:exec
改为:one
,并在最后增加RETURNING *
,如下:
UPDATE accounts SET balance = $2 WHERE id = $1
RETURNING *;
重新生成代码,make sqlc
,可以看到account.sql.go
里面的UpdateAccount
有返回值了。
之后,运行make sqlc
,account.sql.go
文件中已经有了新增的DeleteAccount
,这样一个完整的CRUD
便完成了。还有两个表entries
和transfers
,可以作为练习,自己实现一下CRUD
。
下一节,我们将学习如何,Golang使用随机数据为数据库的CRUD写单元测试。