分享我的工作、生活与心情,让自己变得丰富多彩!
技术 | 2020-03-02 02:35
目录
1. 约定
2. 结构体标签
3. 创建记录
4. 更新
5.删除
6. 查询
7.关联
8.链式操作
9.范围
10.多个立即执行方法的注意事项
11.错误处理
12.钩子
13.事务
14.模型方法
15.原生SQL
16.复合主键
17.Logger
GORM 默认会使用结构体中名为ID的字段作为表的主键
GORM 默认使用蛇形小写
GORM 时间跟踪, 会自动更新create_at update_at
GORM 软删除(delete_at有值)
类型定义,索引: 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 设置嵌入结构的前缀
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;
单字段:
db.Model(&user).Update("name", "hello")
多字段
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false}) //mapdb.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}
使用Model删
// 注意: gorm会根据主键来删, 参数的主键必须有值, 否则会清空所有记录!!!db.Delete(&email)//批删除db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
软删除(就是标记DeleteAt,不实际删除, 这里略去)
简单查询:
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是切片
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 INdb.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)的查询条件 // !!! 例外: 忽略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
//适用于结构体和映射 // 无则创建 db.FirstOrCreate(&user, User{Name: "non_existing"}) // INSERT INTO "users" (name) VALUES ("non_existing"); // user -> User{Id: 112, Name: "non_existing"} // 有则填充结构体变量 db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user) // user -> User{Id: 111, Name: "Jinzhu"} // 使用Attrs() // 未找到 db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user) // SELECT * FROM users WHERE name = 'non_existing'; // INSERT INTO "users" (name, age) VALUES ("non_existing", 20); // user -> User{Id: 112, Name: "non_existing", Age: 20} // 找到,则使用数据库值填充结构体 db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user) // SELECT * FROM users WHERE name = 'jinzhu'; // user -> User{Id: 111, Name: "jinzhu", Age: 20} //使用Assign() // 未找到 db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user) // SELECT * FROM users WHERE name = 'non_existing'; // INSERT INTO "users" (name, age) VALUES ("non_existing", 20); // user -> User{Id: 112, Name: "non_existing", Age: 20} // 找到, 使用结构体值填充数据库 db.Where(User{Name: "jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user) // SELECT * FROM users WHERE name = 'jinzhu'; // UPDATE users SET age=30 WHERE id = 111; // user -> User{Id: 111, Name: "jinzhu", Age: 30}
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 parameterdb.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 []int64db.Find(&users).Pluck("age", &ages) //采集User中的年龄var names []stringdb.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 Resultdb.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)// Raw SQLdb.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
belongs to, 属于(多对一关系。[个人理解:由子表登记父表名,站在儿子的角度说:我属于我的爸爸],应用举例:多个用户对应一个组)
type User struct { //主表,包含两个字段:id,name ID uint Name string}// `Profile` belongs to `User`, `UserID` is the foreign keytype 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 keytype 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).Errorfmt.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 referencedb.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 tabletype 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 tabletype 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 []Userlanguage := 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 Keystype 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 ForeignKeyIf you want to change join table’s foreign keys, you could use tag association_jointable_foreignkey, jointable_foreignkeytype 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-ReferencingTo 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 userdb.Preload("Languages").First(&user)
关联模式(主要是为了使用它的helper方法)
Association Mode contains some helper methods to handle relationship related things easily.// Start Association Modevar user Userdb.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 todb.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 associationstype 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;
//Method Chaining,Gorm 实现了链式操作接口,所以你可以把代码写成这样:db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")// 创建一个新的 relation, 注意:这里赋值给txtx := db.Where("name = ?", "jinzhu")// 添加更多查询条件, 注意:这里赋值给txif 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…
//Scopes()的入参是个func(db *gorm.DB) *gorm.DBfunc 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 的订单
//多个立即执行方法//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%'
//处理数据时,通常会发生多个错误。 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() { // 未找到记录}
省略,就是创建前后,更新前后,删除前后,查询前后调用的钩子 可以自定义钩子,注册即可使用
// 开启事务 tx := db.Begin()// 在事务中执行具体的数据库操作 (事务内的操作使用 'tx' 执行,而不是 'db') tx.Create(...)// ...// 如果发生错误则执行回滚 tx.Rollback()// 或者(未发生错误时)提交事务 tx.Commit()
判断表存在与否,建表,删表,修改列数据类型,删除列,增删索引,增删外键
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)
type Product struct { ID string `gorm:"primary_key"` LanguageCode string `gorm:"primary_key"` Code string Name string}
//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))
小雷竞技官网登入-RAYBET雷竞技在线进入 渝ICP备14008124号-2