Save()默认会更新该对象的所有字段,即使你没有赋值。
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) // 1,定义模型 type User struct { ID int64 Name string Age int64 Active bool } func main() { // 2, 连接数据库 db, err := gorm.Open("mysql", "root:root1234@(127.0.0.1:13306)/db1?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic(err) } defer db.Close() // 默认情况下,gorm创建的表将会是结构体名称的复数形式,如果不想让它自动复数,可以加一下禁用 db.SingularTable(true) // 3, 把模型与数据库中的表对应起来 db.AutoMigrate(&User{}) // 4, 创建 //u1 := User{Name: "eryajf", Age: 20, Active: true} //db.Create(&u1) //u2 := User{Name: "jinzhu", Age: 22, Active: false} //db.Create(&u2) // 5, 查询 var user User db.First(&user) fmt.Println(user) // 6, 更新 user.Name = "二丫讲梵" user.Age = 20 // save 默认会更新该对象的所有字段,即使没有赋值 db.Debug().Save(&user) //UPDATE `user` SET `name` = '二丫讲梵', `age` = 20, `active` = true WHERE `user`.`id` = 1 }
如果你只希望更新指定字段,可以使用Update或者Updates
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) // 1,定义模型 type User struct { ID int64 Name string Age int64 Active bool } func main() { // 2, 连接数据库 db, err := gorm.Open("mysql", "root:root1234@(127.0.0.1:13306)/db1?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic(err) } defer db.Close() // 默认情况下,gorm创建的表将会是结构体名称的复数形式,如果不想让它自动复数,可以加一下禁用 db.SingularTable(true) // 3, 把模型与数据库中的表对应起来 db.AutoMigrate(&User{}) // 4, 创建 //u1 := User{Name: "eryajf", Age: 20, Active: true} //db.Create(&u1) //u2 := User{Name: "jinzhu", Age: 22, Active: false} //db.Create(&u2) // 5, 查询 var user User db.First(&user) fmt.Println(user) // 6, 更新 // 更新单个属性,如果他有变化 db.Debug().Model(&user).Update("name", "hello") //UPDATE `user` SET `name` = 'hello' WHERE `user`.`id` = 1 // 根据给定的条件更新单个属性 db.Debug().Model(&user).Where("active = ?", true).Update("name", "hello") //UPDATE `user` SET `name` = 'hello' WHERE `user`.`id` = 1 AND ((active = true)) // 使用map 更新多个属性,只会更新其中有变化的属性 db.Debug().Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": "false"}) //UPDATE `user` SET `active` = true, `age` = 18, `name` = 'hello' WHERE `user`.`id` = 1 // 使用struct 更新多个属性,只会更新其中有变化且为非零值的字段 db.Debug().Model(&user).Updates(User{Name: "hello", Age: 23}) //UPDATE `user` SET `age` = 23, `name` = 'hello' WHERE `user`.`id` = 1 // 警告:当使用struct更新时,GORM只会更新那些非零值的字段 // 对于下面的操作,不会发生任何更新 db.Debug().Model(&user).Updates(User{Name: "", Age: 0, Active: false}) //db.Debug().Model(&user) }
如果你想更新或忽略某些字段,你可以使用 Select,Omit
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) // 1,定义模型 type User struct { ID int64 Name string Age int64 Active bool } func main() { // 2, 连接数据库 db, err := gorm.Open("mysql", "root:root1234@(127.0.0.1:13306)/db1?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic(err) } defer db.Close() // 默认情况下,gorm创建的表将会是结构体名称的复数形式,如果不想让它自动复数,可以加一下禁用 db.SingularTable(true) // 3, 把模型与数据库中的表对应起来 db.AutoMigrate(&User{}) // 4, 创建 //u1 := User{Name: "eryajf", Age: 20, Active: true} //db.Create(&u1) //u2 := User{Name: "jinzhu", Age: 22, Active: false} //db.Create(&u2) // 5, 查询 var user User db.First(&user) fmt.Println(user) // 6, 更新 m1 := map[string]interface{}{ "name": "liqilong", "age": 20, "active": true, } // 选择 m1 中某个字段进行更新 db.Debug().Model(&user).Select("name").Updates(m1) //UPDATE `user` SET `name` = 'liqilong' WHERE `user`.`id` = 1 // 忽略 m1 中active字段更新其他字段 db.Debug().Model(&user).Omit("active").Updates(m1) //UPDATE `user` SET `age` = 20, `name` = 'liqilong' WHERE `user`.`id` = 1 }
上面的更新操作会自动运行 model 的 BeforeUpdate, AfterUpdate 方法,更新 UpdatedAt 时间戳, 在更新时保存其 Associations, 如果你不想调用这些方法,你可以使用 UpdateColumn, UpdateColumns
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) // 1,定义模型 type User struct { ID int64 Name string Age int64 Active bool } func main() { // 2, 连接数据库 db, err := gorm.Open("mysql", "root:root1234@(127.0.0.1:13306)/db1?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic(err) } defer db.Close() // 默认情况下,gorm创建的表将会是结构体名称的复数形式,如果不想让它自动复数,可以加一下禁用 db.SingularTable(true) // 3, 把模型与数据库中的表对应起来 db.AutoMigrate(&User{}) // 4, 创建 //u1 := User{Name: "eryajf", Age: 20, Active: true} //db.Create(&u1) //u2 := User{Name: "jinzhu", Age: 22, Active: false} //db.Create(&u2) // 5, 查询 var user User db.First(&user) fmt.Println(user) // 6, 更新 // 更新单个属性,类似于 update db.Debug().Model(&user).UpdateColumn("name", "hello") //UPDATE `user` SET `name` = 'hello' WHERE `user`.`id` = 1 // 更新多个属性,类似于 updates db.Debug().Model(&user).UpdateColumns(User{Name: "hello", Age: 18}) //UPDATE `user` SET `age` = 18, `name` = 'hello' WHERE `user`.`id` = 1 }
批量更新时Hooks(钩子函数)不会运行。
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) // 1,定义模型 type User struct { ID int64 Name string Age int64 Active bool } func main() { // 2, 连接数据库 db, err := gorm.Open("mysql", "root:root1234@(127.0.0.1:13306)/db1?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic(err) } defer db.Close() // 默认情况下,gorm创建的表将会是结构体名称的复数形式,如果不想让它自动复数,可以加一下禁用 db.SingularTable(true) // 3, 把模型与数据库中的表对应起来 db.AutoMigrate(&User{}) // 4, 创建 //u1 := User{Name: "eryajf", Age: 20, Active: true} //db.Create(&u1) //u2 := User{Name: "jinzhu", Age: 22, Active: false} //db.Create(&u2) // 5, 查询 var user User db.First(&user) fmt.Println(user) // 6, 更新 db.Debug().Table("user").Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 20}) //UPDATE `user` SET `age` = 20, `name` = 'hello' WHERE (id IN (10,11)) // 使用struct更新时,只会更新非零值字段,若想更新所有字段,应使用如上map方式 db.Debug().Model(User{}).Updates(User{Name: "hello", Age: 23}) //UPDATE `user` SET `age` = 23, `name` = 'hello' // 使用 RowsAffected 获取更新记录总数 chageNum := db.Debug().Model(User{}).Updates(User{Name: "hello", Age: 23}).RowsAffected //UPDATE `user` SET `age` = 23, `name` = 'hello' fmt.Println(chageNum) //db.Debug().Model(&user) }
先查询表中的第一条数据保存至user变量。
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) // 1,定义模型 type User struct { ID int64 Name string Age int64 Active bool } func main() { // 2, 连接数据库 db, err := gorm.Open("mysql", "root:root1234@(127.0.0.1:13306)/db1?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic(err) } defer db.Close() // 默认情况下,gorm创建的表将会是结构体名称的复数形式,如果不想让它自动复数,可以加一下禁用 db.SingularTable(true) // 3, 把模型与数据库中的表对应起来 db.AutoMigrate(&User{}) // 4, 创建 //u1 := User{Name: "eryajf", Age: 20, Active: true} //db.Create(&u1) //u2 := User{Name: "jinzhu", Age: 22, Active: false} //db.Create(&u2) // 5, 查询 var user User db.First(&user) fmt.Println(user) // 6, 更新 // 给匹配到的用户增加3岁 db.Debug().Model(&user).Update("age", gorm.Expr("age + ?", 3)) //UPDATE `user` SET `age` = age + 3 WHERE `user`.`id` = 1 // 以map的方式进行年龄运算 db.Debug().Model(&user).Updates(map[string]interface{}{"age": gorm.Expr("age * ? + ?", 2, 100)}) //UPDATE `user` SET `age` = age * 2 + 100 WHERE `user`.`id` = 1 // 年龄减法 db.Debug().Model(&user).UpdateColumn("age", gorm.Expr("age - ?", "2")) //UPDATE `user` SET `age` = age - '2' WHERE `user`.`id` = 1 // 给符合查询条件的用户年龄-1 db.Debug().Model(&user).Where("age > 10").UpdateColumn("age", gorm.Expr("age - ?", 1)) //UPDATE `user` SET `age` = age - 1 WHERE `user`.`id` = 1 AND ((age > 10)) }
如果你想修改 BeforeUpdate, BeforeSave 等 Hooks 中更新的值,你可以使用 scope.SetColumn, 例如:
func (user *User) BeforeSave(scope *gorm.Scope) (err error) { if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil { scope.SetColumn("EncryptedPassword", pw) } }
// 为 update SQL 添加其它的 SQL db.Model(&user).Set("gorm:update_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Update("name", "hello") //// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111 OPTION (OPTIMIZE FOR UNKNOWN);
警告 删除记录时,请确保主键字段有值,GORM 会通过主键去删除记录,如果主键为空,GORM 会删除该 model 的所有记录。
// 删除现有记录 db.Delete(&email) //// DELETE from emails where id=10; // 为删除 SQL 添加额外的 SQL 操作 db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email) //// DELETE from emails where id=10 OPTION (OPTIMIZE FOR UNKNOWN);
删除全部匹配的记录
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) // 定义模型 type User struct { gorm.Model Name string Age int64 Active bool } func main() { db, err := gorm.Open("mysql", "root:root1234@(127.0.0.1:13306)/db1?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic(err) } defer db.Close() // 默认情况下,gorm创建的表将会是结构体名称的复数形式,如果不想让它自动复数,可以加一下禁用 db.SingularTable(true) // 2, 把模型与数据库中的表对应起来 db.AutoMigrate(&User{}) // 3, 创建 //u1 := User{Name: "eryajf", Age: 20, Active: true} // //db.Create(&u1) // //u2 := User{Name: "jinzhu", Age: 22, Active: false} // //db.Create(&u2) // //u3 := User{Name: "eryajf2", Age: 20, Active: true} // //db.Create(&u3) // //u4 := User{Name: "jinzhu2", Age: 22, Active: false} // //db.Create(&u4) // 删除符合条件的记录 db.Debug().Where("name LIKE ?", "%jinzhu%").Delete(User{}) //UPDATE `user` SET `deleted_at`='2020-03-08 23:48:05' WHERE `user`.`deleted_at` IS NULL AND ((name LIKE 'jinzhu')) // 与上边效果一样 db.Debug().Delete(User{}, "name LIKE ?", "%jinzhu%") //UPDATE `user` SET `deleted_at`='2020-03-08 23:49:34' WHERE `user`.`deleted_at` IS NULL AND ((name LIKE '%jinzhu%')) }
如果一个 model 有 DeletedAt 字段,他将自动获得软删除的功能! 当调用 Delete 方法时, 记录不会真正的从数据库中被删除, 只会将DeletedAt 字段的值会被设置为当前时间
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;
// Unscoped 方法可以物理删除记录 db.Unscoped().Delete(&order) //// DELETE FROM orders WHERE id=10;
代码示例:
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) // 定义模型 type User struct { gorm.Model Name string Age int64 Active bool } func main() { db, err := gorm.Open("mysql", "root:root1234@(127.0.0.1:13306)/db1?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic(err) } defer db.Close() // 默认情况下,gorm创建的表将会是结构体名称的复数形式,如果不想让它自动复数,可以加一下禁用 db.SingularTable(true) // 2, 把模型与数据库中的表对应起来 db.AutoMigrate(&User{}) // 3, 创建 //u1 := User{Name: "eryajf", Age: 20, Active: true} // //db.Create(&u1) // //u2 := User{Name: "jinzhu", Age: 22, Active: false} // //db.Create(&u2) // //u3 := User{Name: "eryajf2", Age: 20, Active: true} // //db.Create(&u3) // //u4 := User{Name: "jinzhu2", Age: 22, Active: false} // //db.Create(&u4) // 批量删除 db.Debug().Where("age = ?", 20).Delete(&User{}) //UPDATE `user` SET `deleted_at`='2020-03-08 23:53:03' WHERE `user`.`deleted_at` IS NULL AND ((age = 20)) // 查询记录时会忽略被软删除的记录 var user User db.Debug().Where("age = 20").Find(&user) //SELECT * FROM `user` WHERE `user`.`deleted_at` IS NULL AND ((age = 20)) fmt.Println(user) // Unscoped 方法可以查询被软删除的记录 db.Debug().Unscoped().Where("age = 20").Find(&user) //SELECT * FROM `user` WHERE (age = 20) fmt.Println(user) // Unscoped 方法可以物理删除记录 db.Debug().Unscoped().Delete(User{}, "name = ?", "eryajf") //DELETE FROM `user` WHERE (name = 'eryajf') }
Method Chaining,Gorm 实现了链式操作接口,所以你可以把代码写成这样:
// 创建一个查询 tx := db.Where("name = ?", "jinzhu") // 添加更多条件 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…
这有一个基于上面链式方法代码的立即执行方法的例子:
tx.Find(&user)
生成的SQL语句如下:
SELECT * FROM users where name = 'jinzhu' AND age = 30 AND active = 1;
Scopes,Scope是建立在链式操作的基础之上的。
基于它,你可以抽取一些通用逻辑,写出更多可重用的函数库。
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) // 查找所有金额大于 1000 的 COD 订单 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%'