快速生成 Go 語言數據庫模型:探索 GORM gen 的便捷神器
- 官方文檔:https://gorm.io/zh_CN/gen/create.html
一、安裝 mysql
1、安裝 mysql
# 拉取 MySQL 鏡像
docker pull mysql
# 運行 MySQL 容器
docker run --name mysql-server -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
# 安裝mysql客戶端
brew install mysql-client
vi .zshrc 或 .bash_profile
export PATH=$PATH:/usr/local/opt/mysql-client/bin/mysql
source .zshrc
# 連接到 MySQL 服務器
mysql -h 127.0.0.1 -P 3306 -u root -p123456
# 啓停命令
docker stop mysql-server
docker rm mysql-server
2、創建數據庫和表
create database test charset utf8;
use test;
CREATE TABLE book
(
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`title` varchar(128) NOT NULL COMMENT '書籍名稱',
`author` varchar(128) NOT NULL COMMENT '作者',
`price` int NOT NULL DEFAULT '0' COMMENT '價格',
`publish_date` datetime COMMENT '出版日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='書籍表';
二、Gen 基本使用
1、gen_demo/cmd/gen/generate.go
-
配置即代碼,我們通常會在項目的
cmd目錄下定義好 Gen 框架生成代碼的配置。 -
例如,我們的項目名稱爲
gen_demo,那麼我們就在gen_demo/cmd/gen/generate.go文件。
package main
// gorm gen configure
import (
"fmt"
"gen_demo/dal/model"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gen"
)
const MySQLDSN = "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True"
func connectDB(dsn string) *gorm.DB {
db, err := gorm.Open(mysql.Open(dsn))
if err != nil {
panic(fmt.Errorf("connect db fail: %w", err))
}
return db
}
func main() {
// 指定生成代碼的具體相對目錄(相對當前文件),默認爲:./query
// 默認生成需要使用WithContext之後纔可以查詢的代碼,但可以通過設置gen.WithoutContext禁用該模式
g := gen.NewGenerator(gen.Config{
// 默認會在 OutPath 目錄生成CRUD代碼,並且同目錄下生成 model 包
// 所以OutPath最終package不能設置爲model,在有數據庫表同步的情況下會產生衝突
// 若一定要使用可以通過ModelPkgPath單獨指定model package的名稱
OutPath: "../../dal/query",
/* ModelPkgPath: "dal/model"*/
// gen.WithoutContext:禁用WithContext模式
// gen.WithDefaultQuery:生成一個全局Query對象Q
// gen.WithQueryInterface:生成Query接口
Mode: gen.WithDefaultQuery | gen.WithQueryInterface,
})
// 通常複用項目中已有的SQL連接配置db(*gorm.DB)
// 非必需,但如果需要複用連接時的gorm.Config或需要連接數據庫同步表信息則必須設置
g.UseDB(connectDB(MySQLDSN))
// 從連接的數據庫爲所有表生成Model結構體和CRUD代碼
// 也可以手動指定需要生成代碼的數據表
g.ApplyBasic(g.GenerateAllTable()...)
// 執行並生成代碼
g.Execute()
}
- 生成代碼
cd cmd/gen
go run generate.go
- 上述命令會在項目目錄下生成
dal目錄,其中dal/query中是 CRUD 代碼,dal/model下則是生成 Model 結構體。
├── cmd
│ └── gen
│ └── generate.go
├── dal
│ ├── model
│ │ └── book.gen.go
│ └── query
│ ├── book.gen.go
│ └── gen.go
├── go.mod
├── go.sum
└── main.go
2、gen_demo/dal/db.go
- 初始化數據庫連接
package dal
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
func ConnectDB(dsn string) *gorm.DB {
db, err := gorm.Open(mysql.Open(dsn))
if err != nil {
panic(fmt.Errorf("connect db fail: %w", err))
}
return db
}
3、main.go
package main
import (
"context"
"fmt"
"gen_demo/dal"
"gen_demo/dal/model"
"gen_demo/dal/query"
"time"
)
// gen demo
// MySQLDSN MySQL data source name
const MySQLDSN = "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True"
func init() {
dal.DB = dal.ConnectDB(MySQLDSN).Debug()
}
func main() {
// 設置默認DB對象
query.SetDefault(dal.DB)
//Create()
//Query()
//GetDefine()
//GetFilter()
GetSearch()
}
func Create() {
// 創建
b1 := model.Book{
Title: "《七米的Go語言之路》",
Author: "七米",
PublishDate: time.Date(2023, 11, 15, 0, 0, 0, 0, time.UTC),
Price: 100,
}
err := query.Book.WithContext(context.Background()).Create(&b1)
if err != nil {
fmt.Printf("create book fail, err:%v\n", err)
return
}
}
func Query() {
// 查詢
//book, err := query.Book.WithContext(context.Background()).First()
// 也可以使用全局Q對象查詢
book, err := query.Q.Book.WithContext(context.Background()).First()
if err != nil {
fmt.Printf("query book fail, err:%v\n", err)
return
}
fmt.Printf("book:%v\n", book)
}
func Update() {
// 更新
ret, err := query.Book.WithContext(context.Background()).
Where(query.Book.ID.Eq(1)).
Update(query.Book.Price, 200)
if err != nil {
fmt.Printf("update book fail, err:%v\n", err)
return
}
fmt.Printf("RowsAffected:%v\n", ret.RowsAffected)
}
func Del() {
// 刪除
ret, err := query.Book.WithContext(context.Background()).Where(query.Book.ID.Eq(1)).Delete()
if err != nil {
fmt.Printf("delete book fail, err:%v\n", err)
return
}
fmt.Printf("RowsAffected:%v\n", ret.RowsAffected)
}
三、自定義 SQL 查詢
1、語法說明
-
Gen 框架使用模板註釋的方法支持自定義 SQL 查詢,我們只需要按對應規則將 SQL 語句註釋到 interface 的方法上即可
-
Gen 將對其進行解析,併爲應用的結構生成查詢 API,通常建議將自定義查詢方法添加到
model模塊下。
註釋語法
-
Gen 爲動態條件 SQL 支持提供了一些約定語法,分爲三個方面:
-
返回結果
-
模板佔位符
-
模板表達式
返回結果
1、gen_demo/dal/model/querier.go
// dal/model/querier.go
package model
import "gorm.io/gen"
// 通過添加註釋生成自定義方法
type Querier interface {
// SELECT * FROM @@table WHERE id=@id
GetByID(id int) (gen.T, error) // 返回結構體和error
// GetByIDReturnMap 根據ID查詢返回map
//
// SELECT * FROM @@table WHERE id=@id
GetByIDReturnMap(id int) (gen.M, error) // 返回 map 和 error
// SELECT * FROM @@table WHERE author=@author
GetBooksByAuthor(author string) ([]*gen.T, error) // 返回數據切片和 error
}
2、gen_demo/cmd/gen/generate.go
// 通過ApplyInterface添加爲book表添加自定義方法
g.ApplyInterface(func(model.Querier) {}, g.GenerateModel("book"))
- 生成代碼
cd cmd/gen
go run generate.go
3、main.go
func GetDefine() {
// 使用自定義的GetBooksByAuthor方法
rets, err := query.Book.WithContext(context.Background()).GetBooksByAuthor("七米")
if err != nil {
fmt.Printf("GetBooksByAuthor fail, err:%v\n", err)
return
}
for i, b := range rets {
fmt.Printf("%d:%v\n", i, b)
}
}
四、模版佔位符
1、gen_demo/dal/model/querier.go
// Filter 自定義Filter接口
type Filter interface {
// SELECT * FROM @@table WHERE @@column=@value
FilterWithColumn(column string, value string) (gen.T, error)
}
2、gen_demo/cmd/gen/generate.go
// 爲`Book`添加 `Filter`接口
g.ApplyInterface(func(model.Filter) {}, g.GenerateModel("book"))
3、main.go
func GetFilter() {
// 使用自定義的GetBooksByAuthor方法
rets, err := query.Book.WithContext(context.Background()).FilterWithColumn("author", "七米")
if err != nil {
fmt.Printf("GetBooksByAuthor fail, err:%v\n", err)
return
}
fmt.Println(rets)
}
五、模版表達式
Gen 爲動態條件 SQL 提供了強大的表達式支持,目前支持以下表達式:
-
if/else -
where -
set -
for
1、gen_demo/dal/model/querier.go
// Searcher 自定義接口
type Searcher interface {
// Search 根據指定條件查詢書籍
//
// SELECT * FROM book
// WHERE publish_date is not null
// {{if book != nil}}
// {{if book.ID > 0}}
// AND id = @book.ID
// {{else if book.Author != ""}}
// AND author=@book.Author
// {{end}}
// {{end}}
Search(book *gen.T) ([]*gen.T, error)
}
2、gen_demo/cmd/gen/generate.go
// 通過ApplyInterface添加爲book表添加Searcher接口
g.ApplyInterface(func(model.Searcher) {}, g.GenerateModel("book"))
3、main.go
func GetSearch() {
b := &model.Book{Author: "張三"}
rets, err := query.Book.WithContext(context.Background()).Search(b)
if err != nil {
fmt.Printf("Search fail, err:%v\n", err)
return
}
for i, b := range rets {
fmt.Printf("%d:%v\n", i, b)
}
}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/ncVMc_Bg5SjKj6W31eq9hA