这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天
Gorm 数据库 ORM 框架
官方标语:The fantastic ORM library for Golang
官网:Gorm
Gorm 支持非常多的特性
创建数据库连接 (gorm.DB):
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
// db 就是 gorm.DB
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
DSN 解释:
- user:用户名
- pass:密码
- dbname:数据库名
- charset:使用的字符集
- parseTime:正确处理 time.Time 参数需要带上
- loc:解析 time.Time 设置系统的位置,如 Local
基础使用
示例结构体:
type Book struct {
ID int64 `gorm:"column:id;primaryKey"`
Name string `gorm:"column:name"`
}
// 用来指定结构体对应的表名
func (b *Book) TableName() string {
return "tb_book"
}
插入
Gorm 使用 Create 方法插入数据,既可以时一个对象,也可以一个切片
err := db.Create(&Book{
Name: "Golang 程序设计",
}).Error
if err != nil {
fmt.Printf("db.Create Error: %s\n", err.Error())
return
}
err = db.Create([]*Book{
{
Name: "Java 程序设计",
},
{
Name: "Kotlin 程序设计",
},
}).Error
if err != nil {
fmt.Printf("db.Create Error: %s\n", err.Error())
}
查询
Gorm 可以通过 Where 方法构建条件表达式,第一个参数为一个预编译 SQL,后面时对应的值,使用 Frist 会使用 limit 限制返回一条数据,同时不存在数据是,会返回 gorm.ErrRecordNotFound 错误,通过 Find 能查询任何数量的数据
var books []*Book
err := db.Find(&books).Error
if err != nil {
fmt.Printf("db.Find Error: %s\n", err.Error())
return
}
for _, book := range books {
fmt.Printf("books: %v", book)
}
book := Book{}
err = db.First(&book).Error
if err != nil {
fmt.Printf("db.Find Error: %s\n", err.Error())
return
}
fmt.Printf("book: %#v\n", book)
修改
Grom 使用 Updata 方法对单个列进行修改,使用 Updates 更新多个列,使用结构体会自动忽略Golang 的默认值,通过使用 map 解决
err := db.Model(&Book{}).Where("name like ?", "Kotlin%").Update("name", "Android 程序设计").Error
if err != nil {
fmt.Printf("db.Update Error: %s\n", err.Error())
return
}
err = db.Model(&Book{}).Updates(map[string]interface{}{
"name": gorm.Expr("name = concat(name, ?)", "(修改)"),
}).Error
if err != nil {
fmt.Printf("db.Updates Error: %s\n", err.Error())
}
删除
Gorm 提供了 Delete 方法删除数据,通过传入具有非默认值的主键值来删除对应的数据,也可以使用 Where 来实现批量删除
// 删除 ID 为 1 的记录
err := db.Delete(&Book{ID: 1}).Error
if err != nil {
fmt.Printf("db.Delete Error: %s\n", err.Error())
return
}
// 删除 ID > 1 的记录
err = db.Where("id > ?", 1).Delete(&Book{}).Error
if err != nil {
fmt.Printf("db.Delete Error: %s\n", err.Error())
}
Model 和数据库表映射
Model 代码生成
使用 db2struct 工具,自动生成
使用该 Shell 能自动生成列对应的字段常量
#!/usr/bin/env zsh
if [ $# -ne 2 ]; then
echo "参数错误, Usage: $0 <ServiceName> <TableName>"
echo "ServiceName : 服务名,不要 douyin- 前缀"
echo "TableName : 表名,不需要 tb_ 前缀"
exit 0
fi
# 将下划写或者中划线装驼峰形式的函数,接受一个参数
to_camel_case() {
echo "$1" | sed -e "s/\b[a-z]/\u&/g" -e "s/[-_]//g"
}
SERVICE_NAME=$1
TABLE_NAME=$2
TABLE_NAME_CAMEL=$(to_camel_case "$TABLE_NAME")
DIR_PATH="cmd/$SERVICE_NAME/model"
if [ ! -d "$DIR_PATH" ]; then
echo "服务 $SERVICE_NAME 文件夹 $DIR_PATH 不存在"
exit 1
fi
cd "$DIR_PATH" || exit
if [ -z "$(whereis db2struct | awk '{print $2}')" ]; then
go install github.com/Shelnutt2/db2struct/cmd/db2struct@latest
fi
db2struct --host=127.0.0.1 --mysql_port=3306 --user=douyin --password=douyin \
--database=douyin --table="tb_$TABLE_NAME" \
--gorm --no-json \
--package=model --struct "$TABLE_NAME_CAMEL" \
--target="$TABLE_NAME.go" || exit
sed -i -e "s/sets the insert table name for this struct type/结构体对应的数据库表名/" "$TABLE_NAME.go"
# 判断是否需要添加 time 包
if grep -q "time.Time" "$TABLE_NAME.go"; then
sed -i "2a import \"time\"\n" "$TABLE_NAME.go"
fi
# 生成类名对应的常量
COLUMN_LIST=$(grep -E "^(\s+\S+){2}\s+\`\S+\`" "$TABLE_NAME.go" | \
sed -E "s/^\s+(\S+)\s+\S+\s+\`.*column:([a-Z_]+).*\`.*$/\t$TABLE_NAME_CAMEL\1 = \"\2\"/g")
{
echo ""
echo "// 结构体字段对应的数据表列名常量"
echo "const ("
echo "$COLUMN_LIST"
echo ")"
} >> "$TABLE_NAME.go"
gofmt -s -w "./$TABLE_NAME.go"
数据库表生成
Gorm 提供了 AutoMigrate 方法,自动生成并更新结构体对应的数据表
err = db.AutoMigrate(&Book{})
if err != nil {
fmt.Printf("db.AutoMigrate Error: %s\n", err.Error())
return
}