GORM使用
mysql連接設置連接池數據
import (
"gorm.io/driver/clickhouse"
"gorm.io/gorm"
)
func main() {
dsn := "tcp://localhost:9000?database=gorm&username=gorm&password=gorm&read_timeout=10&write_timeout=20"
db, err := gorm.Open(clickhouse.Open(dsn), &gorm.Config{})
// 開啟debug
db.Debug()
db.LogMode(true)
// 維護連接池信息
sqlDB, err := db.DB()
// Ping
sqlDB.Ping()
// Close
// sqlDB.Close()
// 返回數據庫統計信息
sqlDB.Stats()
// SetMaxIdleConns 設置空閑連接池中連接的最大數量
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 設置打開數據庫連接的最大數量。
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 設置了連接可復用的最大時間。
sqlDB.SetConnMaxLifetime(time.Hour)
// Auto Migrate
db.AutoMigrate(&User{})
// Set table options
db.Set("gorm:table_options", "ENGINE=Distributed(cluster, default, hits)").AutoMigrate(&User{})
// 插入
db.Create(&user)
// 查詢
db.Find(&user, "id = ?", 10)
// 批量插入
var users = []User{user1, user2, user3}
db.Create(&users)
// ...
}
gorm.Model嵌套
GORM 定義一個 gorm.Model 結構體,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt.
type User struct {
gorm.Model
Name string
}
字段權限控制
GORM 允許您用標簽控制字段級別的權限。這樣您就可以讓一個字段的權限是只讀、只寫、只創建、只更新或者被忽略
type User struct {
Name string `gorm:"<-:create"` // 允許讀和創建
Name string `gorm:"<-:update"` // 允許讀和更新
Name string `gorm:"<-"` // 允許讀和寫(創建和更新)
Name string `gorm:"<-:false"` // 允許讀,禁止寫
Name string `gorm:"->"` // 只讀(除非有自定義配置,否則禁止寫)
Name string `gorm:"->;<-:create"` // 允許讀和寫
Name string `gorm:"->:false;<-:create"` // 僅創建(禁止從 db 讀)
Name string `gorm:"-"` // 通過 struct 讀寫會忽略該字段
}
創建/更新時間追蹤
type User struct {
CreatedAt time.Time // 在創建時,如果該字段值為零值,則使用當前時間填充
UpdatedAt int // 在創建時該字段值為零值或者在更新時,使用當前時間戳秒數填充
Updated int64 `gorm:"autoUpdateTime:nano"` // 使用時間戳填納秒數充更新時間
Updated int64 `gorm:"autoUpdateTime:milli"` // 使用時間戳毫秒數填充更新時間
Created int64 `gorm:"autoCreateTime"` // 使用時間戳秒數填充創建時間
}
創建
1.)創建指定字段
db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")
2.)忽略部分字段
db.Omit("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")
3.)批量插入
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)
var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// 數量為 100
db.CreateInBatches(users, 100)
// batch insert from `[]map[string]interface{}{}`
db.Model(&User{}).Create([]map[string]interface{}{
{"Name": "jinzhu_1", "Age": 18},
{"Name": "jinzhu_2", "Age": 20},
})
4.)使用默認值
type User struct {
ID string `gorm:"default:uuid_generate_v3()"` // db func
FirstName string `gorm:"default:jone"`
LastName string
Age uint8
FullName string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`
}
創建鈎子
GORM 允許用戶定義的鈎子有 BeforeSave, BeforeCreate, AfterSave, AfterCreate 創建記錄時將調用這些鈎子方法
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.UUID = uuid.New()
if u.Role == "admin" {
return errors.New("invalid role")
}
return
}
// 跳過鈎子
DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)
查詢
1.)查詢單個對象
// 獲取第一條記錄(主鍵升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;
// 獲取一條記錄,沒有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;
// 獲取最后一條記錄(主鍵降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
db.Limit(1).Find(&user)
// 檢查 ErrRecordNotFound 錯誤
errors.Is(result.Error, gorm.ErrRecordNotFound)
2.) 主鍵索引
db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;
db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);
3.)檢索全部
// 獲取全部記錄
result := db.Find(&users)
// SELECT * FROM users;
4.)條件查詢
// String 條件
// 獲取第一條匹配的記錄
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
// 獲取全部匹配的記錄
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';
Struct & 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;
// 主鍵切片條件
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22);
// Plain SQL
db.Find(&user, "name = ?", "jinzhu")
// SELECT * FROM users WHERE name = "jinzhu";
// Plain SQL
db.Find(&user, "name = ?", "jinzhu")
// SELECT * FROM users WHERE name = "jinzhu";
// Struct
db.Find(&users, User{Age: 20})
// SELECT * FROM users WHERE age = 20;
// Map
db.Find(&users, map[string]interface{}{"age": 20})
// SELECT * FROM users WHERE age = 20;
5.)零值的使用
// 默認忽略零值
db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu";
// 正確使用:
db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
6.)分頁查詢
// 通過 -1 消除 Limit 條件
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)
db.Offset(3).Find(&users)
// SELECT * FROM users OFFSET 3;
db.Limit(10).Offset(5).Find(&users)
// SELECT * FROM users OFFSET 5 LIMIT 10;
7.)排序分組
// 多個 order
db.Order("age desc").Order("name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
for rows.Next() {
...
}
type Result struct {
Date time.Time
Total int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)
8.)多表聯合查詢
type result struct {
Name string
Email string
}
db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
}
工具使用
原生SQL生成GORM結構體
https://sql2gorm.mccode.info/
https://github.com/cascax/sql2gorm
https://juejin.cn/post/6844903508823506958
https://segmentfault.com/a/1190000021441127
https://www.jianshu.com/p/288b9296e4b4