简介

sqlc 是一款基于 Go 语言开发的开源代码生成器,基于 DDL 为 Go 项目生成 CURD 的代码。在开始之前,你需要编写 Schema 和 Query SQL。

如果你有现成的数据库,想要快速生成 Schema,则可以使用 atlas 来导出你的 Schema:

atlas schema inspect -u "mysql://root:pass@localhost:3306/example" --format '{{ sql . }}' > schema.sql

安装

macOS 上的安装方式:

brew install sqlc

其他平台可以参考官方文档

配置

首先需要为项目创建 sqlc.yaml

version: "2"
sql:
  - engine: "mysql"
    queries: "query.sql"
    schema: "schema.sql"
    gen:
      go:
        out: "tutorial" # 生成代码的目录
        package: "tutorial" # Go 中的包名,可以根据自己的需要自定义

文件后缀可以是 yamlymljson

定义 Schema

创建 schema.sql 文件定义如下(或使用 atlas 导出现有数据库结构):

-- Create "users" table
CREATE TABLE `users` (
    `id` bigint unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(255) NOT NULL,
    `email` varchar(255) NOT NULL,
    `password` varchar(255) NULL,
    `created_at` timestamp NULL,
    `updated_at` timestamp NULL,
    PRIMARY KEY (`id`),
    UNIQUE INDEX `users_email_unique` (`email`)
) CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;

定义 Query

创建 query.sql 文件,并定义相关逻辑的 DML:

-- name: GetUser :one
SELECT * FROM users
WHERE id = ? LIMIT 1;

-- name: ListUsers :many
SELECT * FROM users
ORDER BY id DESC;

-- name: CreateUser :execresult
INSERT INTO users (
  name, email, password
) VALUES (
  ?, ?, ?
);

-- name: DeleteUser :exec
DELETE FROM users
WHERE id = ?;

注意这里面的 -- name: {NAME} :{} 用来声明方法名称和返回类型的。

生成代码

sqlc generate

生成后目录结构如下:

.
├── query.sql
├── schema.sql
├── sqlc.yaml
└── tutorial
    ├── db.go
    ├── models.go
    └── query.sql.go

2 directories, 6 files

db.go 文件中的内容:

// Code generated by sqlc. DO NOT EDIT.
// versions:
//   sqlc v1.26.0

package tutorial

import (
	"context"
	"database/sql"
)

type DBTX interface {
	ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
	PrepareContext(context.Context, string) (*sql.Stmt, error)
	QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
	QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}

func New(db DBTX) *Queries {
	return &Queries{db: db}
}

type Queries struct {
	db DBTX
}

func (q *Queries) WithTx(tx *sql.Tx) *Queries {
	return &Queries{
		db: tx,
	}
}

models.go 文件中的代码如下:

// Code generated by sqlc. DO NOT EDIT.
// versions:
//   sqlc v1.26.0

package tutorial

import (
	"database/sql"
)

type User struct {
	ID        uint64
	Name      string
	Email     string
	Password  sql.NullString
	CreatedAt sql.NullTime
	UpdatedAt sql.NullTime
}

query.sql.go 文件中代码如下:

// Code generated by sqlc. DO NOT EDIT.
// versions:
//   sqlc v1.26.0
// source: query.sql

package tutorial

import (
	"context"
	"database/sql"
)

const createUser = `-- name: CreateUser :execresult
INSERT INTO users (
  name, email, password
) VALUES (
  ?, ?, ?
)
`

type CreateUserParams struct {
	Name     string
	Email    string
	Password sql.NullString
}

func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (sql.Result, error) {
	return q.db.ExecContext(ctx, createUser, arg.Name, arg.Email, arg.Password)
}

const deleteUser = `-- name: DeleteUser :exec
DELETE FROM users
WHERE id = ?
`

func (q *Queries) DeleteUser(ctx context.Context, id uint64) error {
	_, err := q.db.ExecContext(ctx, deleteUser, id)
	return err
}

const getUser = `-- name: GetUser :one
SELECT id, name, email, password, created_at, updated_at FROM users
WHERE id = ? LIMIT 1
`

func (q *Queries) GetUser(ctx context.Context, id uint64) (User, error) {
	row := q.db.QueryRowContext(ctx, getUser, id)
	var i User
	err := row.Scan(
		&i.ID,
		&i.Name,
		&i.Email,
		&i.Password,
		&i.CreatedAt,
		&i.UpdatedAt,
	)
	return i, err
}

const listUsers = `-- name: ListUsers :many
SELECT id, name, email, password, created_at, updated_at FROM users
ORDER BY id DESC
`

func (q *Queries) ListUsers(ctx context.Context) ([]User, error) {
	rows, err := q.db.QueryContext(ctx, listUsers)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []User
	for rows.Next() {
		var i User
		if err := rows.Scan(
			&i.ID,
			&i.Name,
			&i.Email,
			&i.Password,
			&i.CreatedAt,
			&i.UpdatedAt,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

总结

sqlc 目前只能支持 Go 项目的代码生成,也有提供 Kotlin、Python 和 TypeScript,但目前这些都处于 Beta 阶段。不过 sqlc 提供了其他语言插件的能力,可以预见的是,以后生态将会逐步完善。

另外 atlas 的工具也是一个比较好的 DDL 管理工具。

I hope this is helpful, Happy hacking…