简介

sqlc 可以生成从 SQL 生成类型安全代码。它的工作原理是:

  • 使用 SQL 书写查询语句。
  • 运行 sqlc 生成 Go 代码,该代码为这些查询提供类型安全的接口。
  • 在应用程序代码中调用生成的代码与数据库进行交互。

安装

sqlc 有多种安装方式:

macOS:

brew install sqlc

Ubuntu:

sudo snap install sqlc

go安装:

1
2
3
4
# Go >= 1.17:
go install github.com/kyleconroy/sqlc/cmd/sqlc@latest
# Go < 1.17:
go get github.com/kyleconroy/sqlc/cmd/sqlc

docker安装:

docker pull kjconroy/sqlc

其它:

https://github.com/kyleconroy/sqlc/releases

入门使用

插件 sqlc.yaml

首先创建 sqlc.yaml 文件, sqlc 会在当前目录下查找 sqlc.yaml 或 sqlc.json文件。

1
2
3
4
5
6
7
8
# sqlc.yaml
version: 1
packages:
  - path: "tutorial"
    name: "tutorial"
    engine: "mysql"
    schema: "schema.sql"
    queries: "query.sql"

创建sql文件

sqlc需要得知数据库表结构和相应的sql语句,我们创建schema.sql定义表结构:

1
2
3
4
5
CREATE TABLE games (
  id   BIGINT  NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name text    NOT NULL,
  url  text
);

创建 query.sql 包含需要的sql语句:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- name: Getgame :one
SELECT * FROM games
WHERE id = ? LIMIT 1;

-- name: ListGames :many
SELECT * FROM games
ORDER BY name;

-- name: CreateGame :execresult
INSERT INTO games (
  name, url
) VALUES (
  ?, ?
)

-- name: DeleteGame :exec
DELETE FROM games
WHERE id = ?;


-- name: UpdateGame :exec
UPDATE games
set name = ?,
    url = ?
WHERE id = ?;

生成代码

接下来使用 sqlc generate,在根目录下会生成一个tutorial目录,生成后项目目录如下:

1
2
3
4
5
6
7
8
9
.
├── go.mod
├── query.sql
├── schema.sql
├── sqlc.yaml
└── tutorial
    ├── db.go
    ├── models.go
    └── query.sql.go

测试

接下来编写代码来使用生成的数据库代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
---
title: "sqlc初体验"
date: 2023-05-20T15:54:29+08:00
draft: true
tags: [ "sqlc","database","sql" ]
---

## 简介

sqlc 可以生成从 SQL 生成类型安全代码。它的工作原理是:

* 使用 SQL 书写查询语句。
* 运行 sqlc 生成 Go 代码,该代码为这些查询提供类型安全的接口。
* 在应用程序代码中调用生成的代码与数据库进行交互。

## 安装

sqlc 有多种安装方式:

macOS:

`
brew install sqlc
`

Ubuntu:

`
sudo snap install sqlc
`

go安装:

```console
# Go >= 1.17:
go install github.com/kyleconroy/sqlc/cmd/sqlc@latest
# Go < 1.17:
go get github.com/kyleconroy/sqlc/cmd/sqlc

docker安装:

docker pull kjconroy/sqlc

其它:

https://github.com/kyleconroy/sqlc/releases

入门使用

插件 sqlc.yaml

首先创建 sqlc.yaml 文件, sqlc 会在当前目录下查找 sqlc.yaml 或 sqlc.json文件。

1
2
3
4
5
6
7
8
# sqlc.yaml
version: 1
packages:
  - path: "tutorial"
    name: "tutorial"
    engine: "mysql"
    schema: "schema.sql"
    queries: "query.sql"

创建sql文件

sqlc需要得知数据库表结构和相应的sql语句,我们创建schema.sql定义表结构:

1
2
3
4
5
CREATE TABLE games (
  id   BIGINT  NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name text    NOT NULL,
  url  text
);

创建 query.sql 包含需要的sql语句:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- name: Getgame :one
SELECT * FROM games
WHERE id = ? LIMIT 1;

-- name: ListGames :many
SELECT * FROM games
ORDER BY name;

-- name: CreateGame :execresult
INSERT INTO games (
  name, url
) VALUES (
  ?, ?
)

-- name: DeleteGame :exec
DELETE FROM games
WHERE id = ?;


-- name: UpdateGame :exec
UPDATE games
set name = ?,
    url = ?
WHERE id = ?;

生成代码

接下来使用 sqlc generate,在根目录下会生成一个tutorial目录,生成后项目目录如下:

1
2
3
4
5
6
7
8
9
.
├── go.mod
├── query.sql
├── schema.sql
├── sqlc.yaml
└── tutorial
    ├── db.go
    ├── models.go
    └── query.sql.go

测试

接下来编写代码来使用生成的数据库代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package main

import (
	"context"
	"database/sql"
	"log"
	"reflect"
	"sqlc-demo/tutorial"

	_ "github.com/go-sql-driver/mysql"
)

func run() error {
	ctx := context.Background()

	db, err := sql.Open("mysql", "user:password@/dbname")
	if err != nil {
		return err
	}

	queries := tutorial.New(db)

	games, err := queries.ListGames(ctx)
	if err != nil {
		return err
	}
	log.Println(games)

	result, err := queries.CreateGame(ctx, tutorial.CreateGameParams{
		Name: "game1",
		Url:  sql.NullString{
			String: "www.google.com",
			Valid:  true,
		},
	})
	if err != nil {
		return err
	}

	insertedGameID, err := result.LastInsertId()
	if err != nil {
		return err
	}
	log.Println(insertedGameID)

	game, err := queries.GetGame(ctx, insertedGameID)
	if err != nil {
		return err
	}

	log.Println(reflect.DeepEqual(insertedGameID, game.ID))
	return nil
}

func main() {
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

总结

本文简单使用 sqlc 根据定义的sql语句来生成go代码,相比于ent等orm, sqlc 是根据定义的sql语句生成代码,如果数据库代码出现问题,更容易发现问题,也容易进行数据库审计。

参考

1
2
3
4

## 参考

* https://docs.sqlc.dev/en/stable/index.html