「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」
详细的官方文档翻译可以看SQLBoiler官方文档翻译--最好用的Go ORM框架
前言
最近调研了几个 github 上 star 数多的 ORM 框架,虽然功能都很齐全,但是在使用上却存在比较让人头疼的问题,如语法混乱、语法冗余和最令人头疼的需要通过指针去获取返回值。在调研的过程中发现了一个神仙框架 SQLBoiler,它居然是直接返回查询结果的struct对象的,还可以选择是否返回 err,拥有完备好用无歧义的sql条件构造器,堪称 Go 语言版的 MybatisPlus。
但是 SQLBoiler 官方并没有提供中文文档,国内各个技术社区也没有相关的入门教学,故边学习官方英文文档边编写此入门指导。
这篇文章只会介绍 SQLBoiler 代码生成流程和简单的增删查改操作,详细的功能在后续的文章中介绍。
代码生成
这里使用 MySQL 作为数据库,其他数据库配置和依赖可能会有所不同。
环境要求
- Go 1.13以上
- 表名列名使用蛇形命名法
snake_case
数据库表结构
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;
第一步:下载安装代码生成插件
# 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改成对应数据库的名字(如果支持的话)。
第二步:安装依赖
go get github.com/volatiletech/sqlboiler/v4
go get github.com/volatiletech/null/v8
第三步:配置文件
SQLBoiler 会自动识别根路径下的 sqlboiler.toml 配置文件。内容如下:
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目录中的代码:
sqlboiler mysql
增删查改例子
设置数据源
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_test/models"
)
func main() {
// 创建数据源
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/tbname?parseTime=True")
if err != nil {
log.Fatal(err)
}
// 设置全局数据源
boil.SetDB(db)
}
Insert
func InsertUserDemo() {
user := models.User{
Username: "username1",
Password: "123456",
NickName: "nickName1",
}
user.InsertGP(boil.Infer())
fmt.Printf("%+v", user)
}
// 输出
{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())
即可,插入后会自动设置 ID
、 CreateAt
和 UpdatedAt
等自动生成的值。其中 boil.Infer()
是智能选择插入字段,Go 零值字段不会被选中,但是插入后会根据数据库生成的值设置这些字段(如自增主键,默认账,更新时间,创建时间等)。
InsertGP()
的 GP
表示使用全局数据源(G)和不返回 error
(P,当返回 error
不为 nil
时直接 panic
),R
和 L
字段是 SQLBoiler 插件自动生成现在可以先忽略。
Delete
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
批量删除,也可以根据条件
进行删除,返回值是成功删除的行数。
Select
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()
可以用来指定查询的列。
Update
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{}
表示更新的字段和对应的值。
完整代码
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_test/models"
)
func main() {
// 创建数据源
db, err := sql.Open("mysql",
"root:root@tcp(127.0.0.1:3306)/tbname?parseTime=True")
if err != nil {
log.Fatal(err)
}
// 设置全局数据源
boil.SetDB(db)
//InsertUserDemo()
//DeleteUserDemo()
//SelectUserDemo()
//UpdateUserDemo()
}
func InsertUserDemo() {
user := models.User{
Username: "username1",
Password: "123456",
NickName: "nickName1",
}
user.InsertGP(boil.Infer())
fmt.Printf("%+v", user)
}
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)
}
func SelectUserDemo() {
// 根据条件查询(如果不指定条件则查询全部)
users := models.Users(qm.Select(models.UserColumns.ID, models.UserColumns.NickName),
models.UserWhere.Username.EQ("username1")).AllGP()
fmt.Println(users[0])
// 查询数量
count := models.Users(models.UserWhere.Username.EQ("username1")).CountGP()
fmt.Println(count)
// 根据主键查询
user, err := models.FindUserG(1)
if err == sql.ErrNoRows {
// 业务处理
}
fmt.Println(user)
}
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)
}
详细的官方文档翻译可以看SQLBoiler官方文档翻译--最好用的Go ORM框架