SQLBoiler入门指导1-最好用的Go ORM框架

SQLBoiler官方文档翻译–最好用的Go ORM框架


SQLBoiler 是一个根据数据库表生成对应的 Go ORM 代码的工具。

它是一个数据库先行的 ORM 框架,也就是说你需要先设计你的数据库,而不是像 gorm 那样先设计 struct。


1. 环境

这里使用 MySQL 作为数据库,其他数据库配置和依赖可能会有所不同。

环境要求:

  • Go 1.13以上
  • 表名列名使用蛇形命名法snake_case
蛇形命名法

特点

  1. 全部小写:所有字母都使用小写。
  2. 单词间隔:单词之间使用下划线(_)来分隔。

数据库表结构

1
2
3
4
5
6
7
8
9
CREATE TABLE `user`  (
`id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`nick_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
`created_at` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
`updated_at` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

第一步:下载安装代码生成插件,

1
2
3
4
5
6
7
# Go 1.16 和之后:
go install github.com/volatiletech/sqlboiler/v4@latest
go install github.com/volatiletech/sqlboiler/v4/drivers/sqlboiler-mysql@latest

# Go 1.15 和之前:
GO111MODULE=on go get -u -t github.com/volatiletech/sqlboiler/v4
GO111MODULE=on go get github.com/volatiletech/sqlboiler/v4/drivers/sqlboiler-mysql

注:如果使用别的数据库需要把上面的mysql改成对应数据库的名字(如果支持的话)。


第二步:安装依赖

1
2
go get github.com/volatiletech/sqlboiler/v4
go get github.com/volatiletech/null/v8

第三步:配置文件

SQLBoiler 会自动识别根路径下的 sqlboiler.toml 配置文件。内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
wipe     = true # 先删除之前自动生成的文件再重新生成
no-tests = true # 不生成测试代码
add-global-variants = true # 生成使用全局数据源的方法,也就是带 G 后缀的方法
add-panic-variants = true # 生成使用当 error 不为 nil 时 panic 的方法,也就是带 P 后缀的方法
no-context = true # 不需要上下文参数

[mysql]
dbname = "dbname"
host = "127.0.0.1"
user = "root"
pass = "root"
sslmode = "false"

注意:如果使用其他数据库可能配置会有区别,详细的配置项可以看官方文档


第四步:生成代码

在命令行输入下面命令即可生成图片models目录中的代码:

1
sqlboiler mysql 




2. 增删改查

2.1. 设置数据库源

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
package main

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/volatiletech/sqlboiler/v4/boil"
"github.com/volatiletech/sqlboiler/v4/queries/qm"
"log"
"sqlboiler-learning/start/models"
)

func main() {
// 创建数据源
// user:password@protocol(address)/dbname?param=value
dsn := "root:guowei.gongPWD2024@tcp(127.0.0.1:3306)/learning?parseTime=True"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 设置全局数据源
boil.SetDB(db)

//InsertUserDemo()
//DeleteUserDemo()
//SelectUserDemo()
//UpdateUserDemo()
}




2.2. insert

1
2
3
4
5
6
7
8
9
func InsertUserDemo() {
user := models.User{
Username: "username1",
Password: "123456",
NickName: "nickName1",
}
user.InsertGP(boil.Infer())
fmt.Printf("%+v", user)
}
查看输出
1
{ID:7 Username:username1 Password:123456 NickName:nickName1 CreatedAt:2021-07-18 13:34:16.3409056 +0000 UTC UpdatedAt:2021-07-18 13:34:16.3409056 +0000 UTC R:<nil> L:{}}

可以看到插入操作只需一行代码 user.InsertGP(boil.Infer()) 即可,插入后会自动设置 IDCreateAtUpdatedAt 等自动生成的值。其中 boil.Infer() 是智能选择插入字段,Go 零值字段不会被选中,但是插入后会根据数据库生成的值设置这些字段(如自增主键,默认账,更新时间,创建时间等)。

InsertGP()GP 表示使用全局数据源(G)和不返回 error(P,当返回 error 不为 nil 时直接 panic),RL 字段是 SQLBoiler 插件自动生成现在可以先忽略。





2.3. delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func DeleteUserDemo() {
// 根据 User.ID 进行删除
user := models.User{ID: 1}
count := user.DeleteGP()
fmt.Println(count)

// 根据 UserSlice 批量删除
users := models.Users().AllGP()
count = users.DeleteAllGP()
fmt.Println(count)

// 根据条件进行删除
count, _ = models.Users(models.UserWhere.Username.EQ("username1")).DeleteAllG()
fmt.Println(count)
}

删除操作也需要一行,可以根据 User struct 进行删除,根据 UserSlice 批量删除,也可以根据条件进行删除,返回值是成功删除的行数。





2.4. select

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func SelectUserDemo() {
// 根据条件查询(如果不指定条件则查询全部)
users := models.Users(qm.Select(models.UserColumns.ID, models.UserColumns.NickName),
models.UserWhere.Username.EQ("username1")).AllGP()
fmt.Println(users)

// 查询数量
count := models.Users(models.UserWhere.Username.EQ("username1")).CountGP()
fmt.Println(count)

// 根据主键查询
user, err := models.FindUserG(1)
if err == sql.ErrNoRows {
// 业务处理
}
fmt.Println(user)
}

可以根据条件查询记录,查询数量,还可以根据主键查询。注意 FinUserG() 在查询不到记录的时候会返回 sql.ErrNoRows,所以最好判断一下。

qm.Select() 可以用来指定查询的列。





2.5. update

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func UpdateUserDemo() {
// 根据 User.ID 进行更新
user := models.User{ID: 1, NickName: "nickName1001"}
count := user.UpdateGP(boil.Whitelist(models.UserColumns.NickName))
fmt.Println(count)

// 根据 UserSlice 批量更新
users := models.Users().AllGP()
count = users.UpdateAllGP(models.M{models.UserColumns.NickName: "updateAllNickName"})
fmt.Println(count)

// 根据条件进行更新
count, _ = models.Users(models.UserWhere.NickName.EQ("updateAllNickName")).
UpdateAllG(models.M{models.UserColumns.NickName: "updateAllNickName2"})
fmt.Println(count)
}

与 Delete 操作类似,可以根据 User struct 进行更新,也可以根据 UserSlice 批量更新,还可以根据条件进行更新。

这里需要注意,在根据 User struct 进行更新时,最好使用 boil.Whitelist() 指定更新的字段,因为如果使用 boil.Infer() 就算是零值也会进行更新。

models.M{} 表示更新的字段和对应的值。