这是我参与[第五届青训营]笔记创作的第四天
前言
本文介绍了三个 Go 主流开发框架 GORM,Kitex,Hertz 的基本使用方法,包含 ORM,RPC,HTTP 。
ORM
是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
GORM
GORM 是一款面向 Go 开发的,对开发人员友好的,“梦幻般的” ORM 库。
GORM的使用
增删改查
实例
package main
import (
"gorm.io/gorm"
"gorm.io/driver/mysql"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func main() {
dsn := "user:pass@tcp(127.0.0.1:3306)/dbnamecharset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
db.AutoMigrate(&Product{}) // 迁移 schema
db.Create(&Product{Code: "D42", Price: 100}) // Create
// Read
var product Product
db.First(&product, 1) // 根据整型主键查找
db.First(&product, "code = ?", "D42") // 查找`code`字段值为`D42`的记录
db.Model(&product).Update("Price", 200) // Update将`product`的`price`更新为200
//`Update`新多个字段
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
db.Delete(&product, 1)// Delete - 删除 product
}
1、链接数据库
首先我们需要对数据库链接
import (
"gorm.io/gorm"
"gorm.io/driver/mysql"
)
2、声明数据库模型
然后声明一个数据库模型struct
type Product struct {
gorm.Model
Code string
Price uint
}
按约定编程的 GORM
GORM 倾向于约定,而不是配置。 默认情况下,GORM 使用 ID 作为主键,使用结构体名的蛇形复数作为表名,字段名的蛇形作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间
旨在在减少使用框架的开发人员需要做出的决策数量的同时不失去灵活性。
因此,当我们在数据模型中指定 gorm.Model 时,ID, CreatedAt、UpdatedAt , DeletedAt 会被自动创建,并按其名字那样工作,例如 CreatedAt 字段会在我们创建一个记录时自动填充创建时间。
同时你可以通过如下方法为字段指定默认值:
type User struct {
ID int64
Name string `gorm:"default:galeone"`
Age int64 `gorm:"default:18"`
}
或是为字段手动指定列名:
type Product struct {
ID uint `gorm:"primarykey"`
Code string `gorm:"column: code"`
Price uint `gorm:"column: user_id"`
}
3、main函数
在 main 函数中:
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})//调用Open开启一个数据库链接,处理可能出现的异常
if err != nil {
panic("failed to connect database")
}
&gorm.Config{} 为 GORM 启用默认的配置,当然,你也可以指定自己的配置,比如通过传入:
&gorm.Config{
PrepareStmt: true
}
复制代码
4、启用预编译语句
启用预编译语句缓存以提高性能。
迁移 schema
db.AutoMigrate(&Product{})
指定数据库自动迁移数据模型结构。
create
db.Create(&Product{Code: "D42", Price: 100})
以指定数据值在指定数据库的数据表中创建一条记录, ID, CreatedAt 等字段指定数据值,且这些数据值将会在特定时刻被自动填入。
read
var product Product
db.First(&product, 1) // 根据整型主键查找
db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录
First 方法返回符合指定条件的首个记录值;
Where 子句查询:
// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';
// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
复制代码
可通过结构体和 Map 传入条件:
// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
// Slice of primary keys
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22);
当使用结构体作为查询条件时,只会查询结构体内的非零值字段,这意味着字段值为 0, '', false 或其他零值的字段不会被用于构建查询条件。
// Update - 将 product 的 price 更新为 200
db.Model(&product).Update("Price", 200)
// Update - 更新多个字段
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
Delete
db.Delete(&product, 1)//删除 product
拓展:
// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10;
// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";
db.Delete(&User{}, 10)
// DELETE FROM users WHERE id = 10;
db.Delete(&User{}, "10")
// DELETE FROM users WHERE id = 10;
db.Delete(&users, []int{1,2,3})
// DELETE FROM users WHERE id IN (1,2,3);
db.Where("email LIKE ?", "%jinzhu%").Delete(&Email{})
// DELETE from emails where email LIKE "%jinzhu%";
db.Delete(&Email{}, "email LIKE ?", "%jinzhu%")
// DELETE from emails where email LIKE "%jinzhu%";
复制代码
需要注意的是,如果我们指定了 gorm.deletedat 字段(gorm.Model 包含该字段),将启用软删除模式:这意味着,改数据模型调用 Delete 方法时,并不会被真正从数据表中删除,而是会设置 DeletedAt 字段为当前时间,此后,你不能再通过普通的查询方法找到该记录:
// user 的 ID 是 `111`
db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
// 批量删除
db.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
// 在查询时会忽略被软删除的记录
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
Unscoped 可绕过该机制,找到被软删除的记录:
db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;
永久删除匹配的记录:
db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;
复制代码
GORM 事务
GORM - The fantastic ORM library for Golang, aims to be developer friendly. GORM 默认会将单个的 create, update, delete操作封装在事务内进行处理,以确保数据的完整性。 如果你想把多个 create, update, delete 操作作为一个原子操作,Transaction 就是用来完成这个的。
为了保证数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。禁用它,这将获得大约 30%+ 性能提升:
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
SkipDefaultTransaction: true,
})
使用事务的一般流程:
db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
使用手动方式创建,提交和回滚事务:
// 开始事务
tx := db.Begin()
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
tx.Create(...)
// ...
// 遇到错误时回滚事务
tx.Rollback()
// 否则,提交事务
tx.Commit()
复制代码
GORM Hook
Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。
如果您已经为模型定义了指定的方法,它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误,GORM 将停止后续的操作并回滚事务。
钩子方法的函数签名应该是 func(*gorm.DB) error
type User struct {
ID int64
Name string `gorm:"default:galeone"`
Age string `gorm:"default:18"`
}
type Email struct {
ID int64
Name string
Email string
}
func (u *User) BeforeCreate(tx `gorm.DB) (err error) {
if u.Age < 0 {
return errors.New("can't save invalid data")
}
return nil;
}
func (u *User) AfterCreate(tx `gorm.DB) (err error) {
return tx.Create(&Email{ID: u.ID, Email: i.Name + "@***.com"}).Error
}