1. 约定

  • GORM 默认会使用结构体中名为ID的字段作为表的主键
  • GORM 结构体的字段以蛇形小写的形式对应到数据库的字段名
  • GORM 时间跟踪, 会自动更新表的created_at updated_at列(对应结构体字段CreatedAt和UpdatedAt, 类型time.Time)
  • GORM 软删除是指表的deleted_at有值

2. 结构体标签

  • 类型定义,索引: gorm:"type:varchar(100);index" gorm:"type:decimal(7,2)"
  • 自定义列名: gorm:"column:name"
  • 指定string长度: gorm:"size:64"
  • 指定主键: gorm:"primary_key"
  • 唯一约束: gorm:"unique"
  • 默认值: gorm:"default:-1" gorm:"default:'abc123'" 字符串默认值使用单引号或\转义的双引号包裹
  • 索引: gorm:"index:idx_user" idx_user是索引名,多个索引同名则是联合索引
  • 唯一索引: 同索引, gorm:"unique_index"
  • 其他: not null 非空, embedded 嵌入(就是结构体同级别), precision列精度,embedded_prefix 设置嵌入结构的前缀

3. 创建记录

u := User{Name: "Jinzhu", Age: 18}
if db.NewRecord(u) { //=> 判断主键是否存在, 主键为空返回`true` db.NewRecord不会取查数据库,只是检查结构体的主键字段是否非空
db.Create(&u) //创建后, u的结构体会被数据库实际的值填充,比如u.Id已经有值了
}
  • 创建前预处理Hook, 只需要写BeforeCreate方法. 例如:

    func (user *User) BeforeCreate(scope *gorm.Scope) error {
    scope.SetColumn("ID", uuid.New())
    return nil
    }
  • 扩展SQL选项(这里不介绍,给例子):

// 为Instert语句添加扩展SQL选项
db.Set("gorm:insert_option", "ON CONFLICT").Create(&product)
// INSERT INTO products (name, code) VALUES ("name", "code") ON CONFLICT;

4. 更新

  • 单字段:
db.Model(&user).Update("name", "hello")
  • 多字段
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})  //map
db.Model(&user).Updates(User{Name: "hello", Age: 18}) //struct,更新非零值字段
  • 只更新选择的字段
//白名单形式: 使用Select("name"),这样就只更新name,其他字段忽略
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111; //黑名单形式: 使用Omit("name")来避免更新name字段
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
  • 纯净版更新: 避开BeforeUpdate / AfterUpdate钩子方法和关联影响
用UpdateColumn, UpdateColumns替代Update和Updates
  • 批更新(钩子函数不运行,好像不是!!!)
db.Table("users").Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11); // Update with struct only works with none zero values, or use map[string]interface{}
//批更新,我的理解就是多个更新,所以下面用空的User结构体
db.Model(User{}).Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18; // Get updated records count with `RowsAffected`
db.Model(User{}).Updates(User{Name: "hello", Age: 18}).RowsAffected
  • SQL表达式
DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
// UPDATE "products" SET "price" = price * '2' + '100', "updated_at" = '2013-11-17 21:34:10' WHERE "id" = '2'; DB.Model(&product).Updates(map[string]interface{}{"price": gorm.Expr("price * ? + ?", 2, 100)})
// UPDATE "products" SET "price" = price * '2' + '100', "updated_at" = '2013-11-17 21:34:10' WHERE "id" = '2'; DB.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2'; DB.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2' AND quantity > 1;
  • 钩子
//BeforeUpdate, BeforeSave, 使用scope.SetColumn来修改字段
func (u *user) BeforeUpdate(scope *gorm.Scope) (err error) {
scope.SetColumn("CreateAt", time.Now().Unix())
return nil
}

5.删除

  • 使用Model删
// 注意: gorm会根据主键来删, 参数的主键必须有值, 否则会清空所有记录!!!
db.Delete(&email) //批删除
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
  • 软删除(就是标记DeleteAt,不实际删除, 这里略去)

6. 查询

  • 简单查询:
db.First(&user)  //通过主键查询第一条记录,user变量的主键必须有值
db.First(&user, 10) // 查询指定的某条记录(只可在主键为整数型时使用)
db.First(&user, User{Name:"tom",Age:13}) // 结构体作为内联条件
db.First(&user, map[string]interface{}{"Name":"tom"}) // map作为内联条件
db.Last(&user)
db.Take(&user) //随机,// SELECT * FROM users LIMIT 1;
db.Find(&users) //取所有记录,变量users是切片
// *** 注意: 不管是First还是Find,如果传入的是切片,就不会报ErrRecordNotFound错误;如果传入的是结构体,则有可能报ErrRecordNotFound错误
  • WHERE查询:
db.Where("name = ?", "jinzhu").First(&user)
//where()参数的形式:
// 字符串(可能有问号); 例如:
// "id <> ?",3
// "id IN (?)",ids
// "name LIKE ?","%tom%"
// "id > ? AND state = 1", 100
// 结构体和映射:
// &User{Name: "jinzhu", Age: 20} //注意,零值字段不参与条件的构建
// map[string]interface{}{"name": "jinzhu", "age": 20}
  • NOT:
db.Not("name", "jinzhu").First(&user)  //单条件,
name不为"jinzhu",条件可以写成db.Not("name = ?", "jinzhu")
db.Not(User{Name: "jinzhu"}).First(&user) //单条件,结构体
db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users) //多条件,对应SQL的NOT IN
db.Not([]int64{1,2,3}).First(&user) //多条件,NOT IN 主键
  • OR:
 //Or()参数可以写成:
// 文本形式: "role = ?", "super_admin"
// 结构体形式: User{Name: "jinzhu 2"}
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
// 映射形式: map[string]interface{}{"name": "jinzhu 2"}
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users)
  • inline条件:
db.First(&user, 23)  //仅限主键是整型时
db.Find(&user, "name = ?", "jinzhu") //注意事项参考"多个立即执行方法"
db.Find(&users, User{Age: 20}) //结构体条件形式
db.Find(&users, map[string]interface{}{"age": 20}) //map条件形式
  • 多个“立即执行方法”串联(Multiple Immediate Methods)时的注意事项
// “立即执行方法”:基本就是和CRUD相关的,例如Find(),Update()等,非立即执行方法就是类似Where(),Order()之类的
//多个立即执行方法串联,比如Find(&ul).Count(&cnt)注意事项:这种情况,后者(这里是Count)会使用前者(这里是Find)的查询条件(例如Find前的Where)
//
// !!! 忽略inline条件 !!!
//例如下面前面的Find中的inline条件被后面的Count()忽略了:
db.Where("name LIKE ?", "jinzhu%").Find(&users, "id IN (?)", []int{1, 2, 3}).Count(&count)
//会生成:
// SELECT * FROM users WHERE name LIKE 'jinzhu%' AND id IN (1, 2, 3)
// SELECT count(*) FROM users WHERE name LIKE 'jinzhu%'
  • FirstOrCreate
//FirstOrCreate是按照条件找到第一条记录,如果找不到,则使用给定条件创建一条记录。条件只支持结构体和映射
//下面举得例子是使用User结构体:
// type User struct {
// ID uint32
// Age uint32
// Name string
// Job uint32
// } // 无则创建
db.FirstOrCreate(&user, User{Age:3, Name: "tomzzz"})
// INSERT INTO "users" (age,name) VALUES (3,"tomzzz");
// user变量的值从User结构体零值变成User{Id: 112, Age:3, Name: "tomzzz", Job:""} // 有则填充结构体变量
db.Where(User{Name: "tomzzz"}).FirstOrCreate(&user)
//user变量的值从User结构体零值变成User{Id: 112, Age:3, Name: "tomzzz", Job:""} // 使用Attrs()
// 未找到
db.Where(User{Age:20, Name: "tomyyy"}).Attrs(User{Job: "teacher"}).FirstOrCreate(&user)
// SELECT * FROM users WHERE age = 20 AND name = 'tomyyy';
// INSERT INTO "users" (age, name, job) VALUES (20,"tomyyy", "teacher");
// user变量的值从User结构体零值变成User{Id: 113, Age:20, Name: "tomyyy", Job:"teacher"} // 找到,则使用数据库值填充结构体
db.Where(User{Age:20, Name: "tomyyy"}).Attrs(User{Job: "nurse"}).FirstOrCreate(&user)
// SELECT * FROM users WHERE age = 20 AND name = 'tomyyy';
// user变量的值从User结构体零值变成User{Id: 113, Age:20, Name: "tomyyy", Job:"teacher"} , 注意user变量的Job字段,使用了数据库记录的值 //使用Assign()
// 未找到
db.Where(User{Age:22, Name: "tomxxx"}).Assign(User{Job: "lawyer"}).FirstOrCreate(&user)
// SELECT * FROM users WHERE age = 22 AND name = 'tomxxx';
//INSERT INTO "users" (age, name, job) VALUES (22,"tomxxx", "lawyer");
// user变量的值从User结构体零值变成User{Id: 114, Age:22, Name: "tomxxx", Job:"lawyer"} // 找到, 使用Assign指定的值写入数据库
db.Where(User{Age:22, Name: "tomxxx"}).Assign(User{Job: "policeman"}).FirstOrCreate(&user)
// SELECT * FROM users WHERE age = 22 AND name = 'tomxxx';
// UPDATE users SET job = 'policeman' WHERE id = 5 AND age 22 AND name 'tomxxx'
// user变量的值从User结构体零值变成User{Id: 114, Age:22, Name: "tomxxx", Job:"policeman"} , 注意user变量的Job字段,使用了Assign的值,数据库的job字段变成policeman
  • FirstOrInit (参考FirstOrCreate,只是不创建数据库记录)

  • Select

db.Select("name, age").Find(&users)  //plain形式
db.Select([]string{"name", "age"}).Find(&users) //string slice形式
db.Table("users").Select("COALESCE(age,?)", 42).Rows() //指定函数形式
  • Order
db.Order("age desc, name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name; // 串联,Multiple orders
db.Order("age desc").Order("name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name; // 第二个Order()中的第二个参数使用true来重写Order规则
db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
// SELECT * FROM users ORDER BY age desc; (users1)
// SELECT * FROM users ORDER BY age; (users2)
  • Limit
db.Limit(3).Find(&users)
// SELECT * FROM users LIMIT 3; // 使用Limit(-1)取消数目限制
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)
  • Offset
db.Offset(3).Find(&users)
// SELECT * FROM users OFFSET 3; // 使用Offset( -1)取消Offset条件
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
// SELECT * FROM users OFFSET 10; (users1)
// SELECT * FROM users; (users2)
  • Count
//Count要排最后,否则会覆盖Select的列
//这里"多即时方法"共用where条件
db.Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Find(&users).Count(&count)
// SELECT * from USERS WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (users)
// SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count) db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
// SELECT count(*) FROM users WHERE name = 'jinzhu'; (count) db.Table("deleted_users").Count(&count)
// SELECT count(*) FROM deleted_users;
  • Group 和 Having
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
for rows.Next() {
...
} rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
for rows.Next() {
...
} //我推荐这种,下面的Scan是将结果集扫描到定义的结构体中
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)
  • Join
rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
...
} db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results) // multiple joins with parameter
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
  • Pluck 采集
var ages []int64
db.Find(&users).Pluck("age", &ages) //采集User中的年龄 var names []string
db.Model(&User{}).Pluck("name", &names) db.Table("deleted_users").Pluck("name", &names) // 如果是多列,则应该使用Select+Find形式
db.Select("name, age").Find(&users)
  • Scan 扫描结果集到 "另一个" 结构体
type Result struct {
Name string
Age int
} var result Result
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result) // Raw SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)

7.关联

  • belongs to, 属于(多对一关系。[个人理解:由子表登记父表名,站在儿子的角度说:我属于我的爸爸],应用举例:多个用户对应一个组)
type User struct {  //主表,包含两个字段:id,name
ID uint
Name string
} // `Profile` belongs to `User`, `UserID` is the foreign key
type Profile struct { //从表,包含三个字段:id,user_id,name
ID uint
UserID int //外键, 默认格式为:类型名+主键名 这里是(UserID)
User User
Name string
} p := Profile{Name: "p1", User: User{Name: "tom"}}
db.Create(&p)
fmt.Printf("%+v\n", p) //回显{ID:2 UserID:2 User:{ID:2 Name:tom} Name:p1}
...
db.Model(&user).Related(&profile) //SELECT * FROM profiles WHERE user_id = 2; 注意,这里不会更新user变量的Name字段,所以这里的user可以写成User{ID:2} //上面提到外键的默认名称是 类型名+主键名,所以下面这样是可以的:
type User struct {
IE uint `gorm:"primary_key"`
Name string
} type Profile struct {
ID uint
UserIE int //外键,注意这里是UserIE
User User
Name string
} //自定义外键
type User struct {
IE uint `gorm:"primary_key"`
Name string
} type Profile struct {
ID uint
UserID int
User User `gorm:"foreignkey:UserID"` //自定义外键名称
Name string
} //Association ForeignKey(从表的外键来源来源于主表的非主键)
type User struct {
IE uint `gorm:"primary_key"`
Name string
} // `Profile` belongs to `User`, `UserID` is the foreign key
type Profile struct {
ID uint
User User `gorm:"association_foreignkey:Name"` //默认用User的主键,将Profile中User的ID存入User的ID,这里改为用Profile中User的Name存储到User的Name中
UserName string //因为上面用User的Name,所以这里就变成UserName了
Name string
}
  • has one, 有一个(一对一关系,语义不同[个人理解:父表中登记子表名,站在父亲的角度说:我有一个儿子],应用举例:一个人只有一张身份证)
//这里假设一个人只有一张信用卡
type User struct { //主表,一个字段:id
ID uint
CreditCard CreditCard
} type CreditCard struct { //从表,三个字段:id,number,user_id
ID uint
Number string
UserID uint //外键,对应User的ID
} u := User{CreditCard: CreditCard{Number: "6666", UserID: 3}} //这里指定UserID=3没用,会被真正的User的ID给覆盖掉
err := db.Create(&u).Error
fmt.Printf("%+v\n", u) //回显: {ID:1 CreditCard:{ID:1 Number:6666 UserID:1}}
...
db.Model(&user).Related(&card, "CreditCard") //SELECT * FROM credit_cards WHERE user_id = 123; 这里"CreditCard"可以省略,以内结构体字段名和类型名同名 //指定外键
type CreditCard struct {
ID uint
Number string
UserName string //外键,对应User的Name
} type User struct {
ID uint
Name string
CreditCard CreditCard `gorm:"foreignkey:UserName"` //默认是使用has one的模型类型加上它的主键,这里使用UserName
} //Association ForeignKey 和上线“属于”中的介绍类似,更改“以哪个字段存入主表” //Polymorphism Association,(个人理解:多主表,单子表)
Supports polymorphic has many and has one associations. type Cat struct {
ID int
Name string
Toy Toy `gorm:"polymorphic:Owner;"`
} type Dog struct {
ID int
Name string
Toy Toy `gorm:"polymorphic:Owner;"`
} type Toy struct {
ID int
Name string
OwnerID int
OwnerType string //如果是猫拥有,这里的值就是"cats"
}
Note: polymorphic belongs-to and many-to-many are explicitly NOT supported, and will throw errors.
  • has many(单对多,一个对应零个或多个)
type User struct {
ID uint
CreditCards []CreditCard //一个人有多张卡
} type CreditCard struct {
ID uint
Number string
UserID uint
} //指定外键字段,略 //Association ForeignKey,略 //Polymorphism Association 多主表共享一个从表
//GORM supports polymorphic has-many and has-one associations.
type Cat struct {
ID int
Name string
Toy []Toy `gorm:"polymorphic:Owner;"`
} type Dog struct {
ID int
Name string
Toy []Toy `gorm:"polymorphic:Owner;"`
} type Toy struct {
ID int
Name string
OwnerID int
OwnerType string //取值"cats"或"dogs"
}
Note: polymorphic belongs-to and many-to-many are explicitly NOT supported, and will throw errors. //使用
db.Model(&user).Related(&emails)
// SELECT * FROM emails WHERE user_id = 111; // 111 is user's primary key
  • 自动创建更新
自动创建/更新
创建/更新记录时, GORM 将自动保存关联及其引用。如果关联具有主键, GORM 将调用 Update 来保存它, 否则将创建它。 user := User{
Name: "jinzhu",
BillingAddress: Address{Address1: "Billing Address - Address 1"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "jinzhu@example.com"},
{Email: "jinzhu-2@example.com"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
} db.Create(&user)
// BEGIN TRANSACTION;
// INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1");
// INSERT INTO "addresses" (address1) VALUES ("Shipping Address - Address 1");
// INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com");
// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu-2@example.com");
// INSERT INTO "languages" ("name") VALUES ('ZH');
// INSERT INTO user_languages ("user_id","language_id") VALUES (111, 1);
// INSERT INTO "languages" ("name") VALUES ('EN');
// INSERT INTO user_languages ("user_id","language_id") VALUES (111, 2);
// COMMIT; //跳过自动更新(只更新引用):
//如果数据库中已存在关联, 你可能不希望对其进行更新。
//可以使用 DB 设置, 将 gorm: association_autoupdate 设置为 false
// Don't update associations having primary key, but will save reference
db.Set("gorm:association_autoupdate", false).Create(&user)
db.Set("gorm:association_autoupdate", false).Save(&user) //或者使用 GORM tags gorm:"association_autoupdate:false"
type User struct {
gorm.Model
Name string
CompanyID uint
// Don't update associations having primary key, but will save reference
Company Company `gorm:"association_autoupdate:false"`
}
  • 多对多(待完善)
Many To Many
Many to Many adds a join table between two models. For example, if your application includes users and languages, and a user can speak many languages, and many users can speak a specfied language. // User has and belongs to many languages, use `user_languages` as join table
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
} type Language struct {
gorm.Model
Name string
}
Back-Reference
// User has and belongs to many languages, use `user_languages` as join table
type User struct {
gorm.Model
Languages []*Language `gorm:"many2many:user_languages;"`
} type Language struct {
gorm.Model
Name string
Users []*User `gorm:"many2many:user_languages;"`
} var users []User
language := Language{} db.First(&language, "id = ?", 111) db.Model(&language).Related(&users, "Users")
// SELECT * FROM "users" INNER JOIN "user_languages" ON "user_languages"."user_id" = "users"."id" WHERE ("user_languages"."language_id" IN ('111'))
Foreign Keys
type CustomizePerson struct {
IdPerson string `gorm:"primary_key:true"`
Accounts []CustomizeAccount `gorm:"many2many:PersonAccount;association_foreignkey:idAccount;foreignkey:idPerson"`
} type CustomizeAccount struct {
IdAccount string `gorm:"primary_key:true"`
Name string
}
It will create a many2many relationship for those two structs, and their relations will be saved into join table PersonAccount with foreign keys customize_person_id_person AND customize_account_id_account Jointable ForeignKey
If you want to change join table’s foreign keys, you could use tag association_jointable_foreignkey, jointable_foreignkey type CustomizePerson struct {
IdPerson string `gorm:"primary_key:true"`
Accounts []CustomizeAccount `gorm:"many2many:PersonAccount;foreignkey:idPerson;association_foreignkey:idAccount;association_jointable_foreignkey:account_id;jointable_foreignkey:person_id;"`
} type CustomizeAccount struct {
IdAccount string `gorm:"primary_key:true"`
Name string
}
Self-Referencing
To define a self-referencing many2many relationship, you have to change association’s foreign key in the join table. to make it different with source’s foreign key, which is generated using struct’s name and its primary key, for example: type User struct {
gorm.Model
Friends []*User `gorm:"many2many:friendships;association_jointable_foreignkey:friend_id"`
}
GORM will create a join table with foreign key user_id and friend_id, and use it to save user’s self-reference relationship. Then you can operate it like normal relations, e.g: DB.Preload("Friends").First(&user, "id = ?", 1) DB.Model(&user).Association("Friends").Append(&User{Name: "friend1"}, &User{Name: "friend2"}) DB.Model(&user).Association("Friends").Delete(&User{Name: "friend2"}) DB.Model(&user).Association("Friends").Replace(&User{Name: "new friend"}) DB.Model(&user).Association("Friends").Clear() DB.Model(&user).Association("Friends").Count()
Working with Many To Many
db.Model(&user).Related(&languages, "Languages")
// SELECT * FROM "languages" INNER JOIN "user_languages" ON "user_languages"."language_id" = "languages"."id" WHERE "user_languages"."user_id" = 111 // Preload Languages when query user
db.Preload("Languages").First(&user)
  • 关联模式(主要是为了使用它的helper方法)
Association Mode contains some helper methods to handle relationship related things easily.

// Start Association Mode
var user User
db.Model(&user).Association("Languages")
// `user` is the source, must contains primary key //user是源,必须包含主键
// `Languages` is source's field name for a relationship //languages是user的字段
// AssociationMode can only works if above two conditions both matched, check it ok or not: //关联模式必须满足上述两个条件
// db.Model(&user).Association("Languages").Error 查找关联
查找匹配的关联 db.Model(&user).Association("Languages").Find(&languages)
添加关联
为many to many,has many添加新的关联关系代替当前的关联关系has one,belongs to db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Append(Language{Name: "DE"}) //替换关联 (关联要有主键,否则就变成新增[原有关联还在,只是其"外键"清空了])
db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN) //删除关联 删除关联的引用,不会删除关联本身 (关联要有主键)
db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Delete(languageZH, languageEN) //清空关联, 清空对关联的引用,不会删除关联本身 (关联要有主键)
db.Model(&user).Association("Languages").Clear() db.Model(&user).Association("Languages").Count()
  • 预加载(加载结构体下的结构体、或结构体下的数组)
//Find()提供外键列表,这个列表作为从表的过滤条件
db.Preload("Orders").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4); //Preload第一个参数之后的参数作为第一参数的where语句
db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled'); //这里的where作用于Find()的参数
db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// SELECT * FROM users WHERE state = 'active';
// SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled'); //多个字段需要居家在,则可以串起来
db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
// SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to //Auto Preloading
//Always auto preload associations
type User struct {
gorm.Model
Name string
CompanyID uint
Company Company `gorm:"PRELOAD:false"` // not preloaded,这个只对Set()起作用,对强制Preload()不生效
Role Role // preloaded
}
db.Set("gorm:auto_preload", true).Find(&users) //Nested Preloading, 选择性的加载"结构体.结构体"
db.Preload("Orders.OrderItems").Find(&users)
db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users) //Custom Preloading SQL 定制预加载行为
//You could custom preloading SQL by passing in func(db *gorm.DB) *gorm.DB, for example: db.Preload("Orders", func(db *gorm.DB) *gorm.DB {
return db.Order("orders.amount DESC")
}).Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4) order by orders.amount DESC;

8.链式操作

//Method Chaining,Gorm 实现了链式操作接口,所以你可以把代码写成这样:
db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable") // 创建一个新的 relation, 注意:这里赋值给tx
tx := db.Where("name = ?", "jinzhu") // 添加更多查询条件, 注意:这里赋值给tx
if someCondition {
tx = tx.Where("age = ?", 20)
} else {
tx = tx.Where("age = ?", 30)
} if yetAnotherCondition {
tx = tx.Where("active = ?", 1)
}
//在调用立即执行方法前不会生成 Query 语句,有时候这会很有用。
//比如你可以抽取一个函数来处理一些通用逻辑。 //立即执行方法 : 相对链式方法而言
//Immediate methods ,立即执行方法是指那些会立即生成 SQL //语句并发送到数据库的方法, 他们一般是 CRUD 方法,比如:
//Create, First, Find, Take, Save, UpdateXXX, Delete, Scan, Row, Rows…

9.范围

//Scopes()的入参是个func(db *gorm.DB) *gorm.DB
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
return db.Where("amount > ?", 1000)
} func PaidWithCreditCard(db *gorm.DB) *gorm.DB {
return db.Where("pay_mode_sign = ?", "C")
} func PaidWithCod(db *gorm.DB) *gorm.DB {
return db.Where("pay_mode_sign = ?", "C")
} func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {
return func (db *gorm.DB) *gorm.DB {
return db.Scopes(AmountGreaterThan1000).Where("status IN (?)", status)
}
} db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders)
// 查询所有信用卡中金额大于 1000 的订单 db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)
// 查询所有 Cod 中金额大于 1000 的订单 db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
// 查询所有已付款、已发货中金额大于 1000 的订单

10.多个立即执行方法的注意事项

//多个立即执行方法
//Multiple Immediate Methods,在 GORM //中使用多个立即执行方法时,后一个立即执行方法会复用前一个 立即执行方法的条件 (不包括内联条件) 。 db.Where("name LIKE ?", "jinzhu%").Find(&users, "id IN (?)", []int{1, 2, 3}).Count(&count)
生成的 Sql //SELECT * FROM users WHERE name LIKE 'jinzhu%' AND id IN (1, 2, 3) //SELECT count(*) FROM users WHERE name LIKE 'jinzhu%'

11.错误处理

//处理数据时,通常会发生多个错误。 GORM提供了一个API来将所有错误作为切片返回:
// 如果发生了一个以上的错误, `GetErrors` 以`[]error`形式返回他们
errors := db.First(&user).Limit(10).Find(&users).GetErrors() fmt.Println(len(errors)) for _, err := range errors {
fmt.Println(err)
} //未找到,gorm.DB.RecordNotFound()返回布尔值
if db.Model(&user).Related(&credit_card).RecordNotFound() {
// 未找到记录
}

12.钩子

省略,就是创建前后,更新前后,删除前后,查询前后调用的钩子
可以自定义钩子,注册即可使用

13.事务

// 开启事务
tx := db.Begin() // 在事务中执行具体的数据库操作 (事务内的操作使用 'tx' 执行,而不是 'db')
tx.Create(...)
// ...
// 如果发生错误则执行回滚
tx.Rollback()
// 或者(未发生错误时)提交事务
tx.Commit()

14.模型方法

判断表存在与否,建表,删表,修改列数据类型,删除列,增删索引,增删外键

15.原生SQL

db.Exec("DROP TABLE users;")
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now(), []int64{11,22,33}) // Scan
type Result struct {
Name string
Age int
} var result Result
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result) //gorm.DB.Row()单行,Rows()是多行
row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
row.Scan(&name, &age)

16.复合主键

type Product struct {
ID string `gorm:"primary_key"`
LanguageCode string `gorm:"primary_key"`
Code string
Name string
}

17.Logger

//Logger
//Gorm有内置的日志记录器支持,默认情况下,它会打印发生的错误 // 启用Logger,显示详细日志
db.LogMode(true) // 禁用日志记录器,不显示任何日志
db.LogMode(false) // 调试单个操作,显示此操作的详细日志
db.Debug().Where("name = ?", "jinzhu").First(&User{})
//自定义 Logger
//参考GORM的默认记录器如何自定义它 https://github.com/jinzhu/gorm/blob/master/logger.go //例如,使用Revel的Logger作为GORM的输出 db.SetLogger(gorm.Logger{revel.TRACE})
//使用 os.Stdout 作为输出 db.SetLogger(log.New(os.Stdout, "\r\n", 0))

gorm整理的更多相关文章

  1. 修改gorm支持protobuf

    gorm的功能很强大,支持很多很多特性,打算在项目中用上它. 但gorm不支持protobuf,如果idl用的是protobuf,需要对每个message做一个重新定义一个内部的struct,使得可以 ...

  2. Go微服务框架go-kratos实战03:使用 gorm 实现增删改查操作

    一.简介 在上一篇文章 go-kratos实战02 中,详细介绍了用 kratos 编写项目代码的步骤.这篇就在上篇基础上,再结合 Go 数据库操作库 gorm 一步一步来实现一个简单的增删改查操作. ...

  3. dotNET跨平台相关文档整理

    一直在从事C#开发的相关技术工作,从C# 1.0一路用到现在的C# 6.0, 通常情况下被局限于Windows平台,Mono项目把我们C#程序带到了Windows之外的平台,在工作之余花了很多时间在M ...

  4. UWP学习目录整理

    UWP学习目录整理 0x00 可以忽略的废话 10月6号靠着半听半猜和文字直播的补充看完了微软的秋季新品发布会,信仰充值成功,对UWP的开发十分感兴趣,打算后面找时间学习一下.谁想到学习的欲望越来越强 ...

  5. SQL Server 常用内置函数(built-in)持续整理

    本文用于收集在运维中经常使用的系统内置函数,持续整理中 一,常用Metadata函数 1,查看数据库的ID和Name db_id(‘DB Name’),db_name('DB ID') 2,查看对象的 ...

  6. kafka学习笔记:知识点整理

    一.为什么需要消息系统 1.解耦: 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 2.冗余: 消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险. ...

  7. JAVA程序员常用软件整理下载

    ********为了大家学习方便,特意整理软件下载如下:*************Java类软件:-------------------------------JDK7.0:http://pan.ba ...

  8. js数组学习整理

    原文地址:js数组学习整理 常用的js数组操作方法及原理 1.声明数组的方式 var colors = new Array();//空的数组 var colors = new Array(3); // ...

  9. GJM : C#设计模式汇总整理——导航 【原创】

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  10. 整理下.net分布式系统架构的思路

    最近看到有部分招聘信息,要求应聘者说一下分布式系统架构的思路.今天早晨正好有些时间,我也把我们实际在.net方面网站架构的演化路线整理一下,只是我自己的一些想法,欢迎大家批评指正. 首先说明的是.ne ...

随机推荐

  1. es7如何使用await发送请求

    handleLogin() { this.$http.post("login", this.formLabelAlign).then(res => { const { dat ...

  2. 【小测试】golang中数组边界检查的开销大约是1.87%~3.12%

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 对比C/C++, golang等类型安全的语言会在数组访问 ...

  3. 2024-01-27:用go语言,阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有N堆金币, 第i堆金币的总重量和总价值分别是m[i]、v[i], 阿里巴巴有一个承重量为T的背包,但并不一定有办法将全部的

    2024-01-27:用go语言,阿里巴巴走进了装满宝藏的藏宝洞.藏宝洞里面有N堆金币, 第i堆金币的总重量和总价值分别是m[i].v[i], 阿里巴巴有一个承重量为T的背包,但并不一定有办法将全部的 ...

  4. 验证码识别服务2Captcha框架

    2Captcha是一个自动验证码识别服务,主要用于解决各种互联网服务中的验证码问题.在许多网站注册账户或进行敏感操作时,为了验证用户是真实的而不是自动化程序,会出现验证码.用户必须正确输入验证码,才能 ...

  5. C/C++ 发送与接收HTTP/S请求

    HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的协议.它是一种无状态的.应用层的协议,用于在计算机之间传输超文本文档,通常在 Web 浏览器和 Web 服务器之 ...

  6. 21.6 Python 构建ARP中间人数据包

    ARP中间人攻击(ARP spoofing)是一种利用本地网络的ARP协议漏洞进行欺骗的攻击方式,攻击者会向目标主机发送虚假ARP响应包,使得目标主机的ARP缓存中的IP地址和MAC地址映射关系被篡改 ...

  7. Windows10安装Apache2.4.54并配置PHP5.6.40/PHP8.1.11

    环境 Windows 10 Apache2.4.54 PHP5.6.40/PHP8.1.11 安装Microsoft Visual C++ 下载地址:https://learn.microsoft.c ...

  8. Hello,World! 6.28

    代码 public class Hello{ public static void main(String[] args){ System.out.print("Hello,World!&q ...

  9. c语言实现内存池

    概要 所谓内存池,顾名思义和线程池的设计原理是一样的,为了减少频繁申请释放内存而带来的资源消耗,减少释放内存后产生的内存碎片. 设计理念 为了方便管理内存池的设计通常是划分出一定数量的内存块,这些内存 ...

  10. Delphi批量替换工具Cnpack

    操,delphi官方 没有 批量替换工具,需要用到cnpack才可以,