中文文档地址:https://jasperxu.github.io/gorm-zh/
英文Doc: https://gorm.io/docs/
(逐步更新)
1. 基本操作
1)连接数据库
连接后可以设置最大连接数等参数
db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
2) 读取数据
读取数据需要定义struct,通过映射Struct参数与table列的名称,为struct对象赋值,列名是Struct参数名的蛇形小写+下划线。也可以加备注`gorm:"column:
a) 指定表名称:配置struct连接table名称,为struct配置TableName()方法:
func () TableName() string { return "" }
db.Model(&struct name here>{})
b) 指定列名称:(来自手册):
type User struct {
ID uint // 列名为 `id`
Name string // 列名为 `name`
Birthday time.Time // 列名为 `birthday`
CreatedAt time.Time // 列名为 `created_at`
}
// 重设列名
type Animal struct {
AnimalId int64 `gorm:"column:beast_id"` // 设置列名为`beast_id`
Birthday time.Time `gorm:"column:day_of_the_beast"` // 设置列名为`day_of_the_beast`
Age int64 `gorm:"column:age_of_the_beast"` // 设置列名为`age_of_the_beast`
}
c) 如何获取查询到的数据?
用api,user可以是struct的slice或者单一对象:
// 获取第一条记录,按主键排序
db.First(&user)
//// SELECT * FROM users ORDER BY id LIMIT 1;
// 获取最后一条记录,按主键排序
db.Last(&user)
//// SELECT * FROM users ORDER BY id DESC LIMIT 1;
// 获取所有记录
db.Find(&users)
//// SELECT * FROM users;
// 使用主键获取记录 / 这个可以直接用where做。。
db.First(&user, 10)
//// SELECT * FROM users WHERE id = 10;
也可以选择用Scan将结果扫描到结构体中(变量名要匹配)
type Result struct {
Name string
Age int
}
var result Result
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)
sql返回多条数据时,可以将结果扫描进数组,另一种方式是使用.Rows(),逐行读取数据。
3)Update
只要执行就update,更新user
db.Save(&user)
也有update方法,如果有筛选条件(where),夹在update前。
db.Model(&user).Update("name", "hello")
其他update方法可以check官方文档
4) 读锁
Select for Update
db.First(&model).ForUpdate()
拓展用法:
1. Batch Insert
参考:https://github.com/jinzhu/gorm/issues/255#issuecomment-481159929
type OwnDb struct {
*gorm.DB
}
func (db *OwnDb)BatchInsert(objArr []interface{}) (int64, error) {
// If there is no data, nothing to do.
if len(objArr) == 0 {
return 0, errors.New("insert a slice length of 0")
}
mainObj := objArr[0]
mainScope := db.NewScope(mainObj)
mainFields := mainScope.Fields()
quoted := make([]string, 0, len(mainFields))
for i := range mainFields {
// If primary key has blank value (0 for int, "" for string, nil for interface ...), skip it.
// If field is ignore field, skip it.
if (mainFields[i].IsPrimaryKey && mainFields[i].IsBlank) || (mainFields[i].IsIgnored) {
continue
}
quoted = append(quoted, mainScope.Quote(mainFields[i].DBName))
}
placeholdersArr := make([]string, 0, len(objArr))
for _, obj := range objArr {
scope := db.NewScope(obj)
fields := scope.Fields()
placeholders := make([]string, 0, len(fields))
for i := range fields {
if (fields[i].IsPrimaryKey && fields[i].IsBlank) || (fields[i].IsIgnored) {
continue
}
var vars interface{}
if (fields[i].Name == "CreatedAt" || fields[i].Name == "UpdatedAt") && fields[i].IsBlank {
vars = gorm.NowFunc()
} else {
vars = fields[i].Field.Interface()
}
placeholders = append(placeholders, scope.AddToVars(vars))
}
placeholdersStr := "(" + strings.Join(placeholders, ", ") + ")"
placeholdersArr = append(placeholdersArr, placeholdersStr)
// add real variables for the replacement of placeholders' '?' letter later.
mainScope.SQLVars = append(mainScope.SQLVars, scope.SQLVars...)
}
mainScope.Raw(fmt.Sprintf("INSERT INTO %s (%s) VALUES %s",
mainScope.QuotedTableName(),
strings.Join(quoted, ", "),
strings.Join(placeholdersArr, ", "),
))
//Execute and Log
if err :=mainScope.Exec().DB().Error;err != nil {
return 0,err
}
return mainScope.DB().RowsAffected,nil
}
如何用?
db,err := gorm.Open(c.Driver, uri)
if err != nil { panic("Database connection failed") } db.LogMode(c.LogMode) DB := &OwnDb{DB:db}
yourDataList := make([]interface{}, len(...))
向这里append
batchInsert(db, yourDataList)
2. Transaction
一个思路:把rollback写入defer recover / err中,一旦发生panic就回滚rollback,执行全部命令就commit
// begin a transaction
tx := db.Begin()
// do some database operations in the transaction (use 'tx' from this point, not 'db')
tx.Create(...)
// ...
// rollback the transaction in case of error
tx.Rollback()
// Or commit the transaction
tx.Commit()
func Transaction(){
conn, tx := NewTransaction(db.Begin())
defer tx.Close()
if err := conn.Create(&xxx).Error; err != nil {
tx.Fail()
}
if err := conn.Update(&yyy); err != nil {
tx.Fail()
}
if err := conn.tx.Delete(&zzz); err != nil {
tx.Fail()
}
//add other err you want to handle here
})
}