for _, user := range users { fmt.Println(user.ID) // 返回插入数据的主键,只有在调用 Create 方法之后才会自动生成 ID } fmt.Println(result.Error) // 返回错误结果 fmt.Println(result.RowsAffected) // 返回影响了多少行
var u0 User db.Find(&u0, 1) // 查询的是 id = 1 // SELECT * FROM `users` WHERE `users`.`id` = 1 db.Find(&u0, 2) // 想查询的是 id = 2,实际上查询的是 id = 1 AND id = 2 // SELECT * FROM `users` WHERE `users`.`id` = 2 AND `users`.`id` = 1
// 获取第一条匹配的记录,三种写法:string、struct、map var u6 User db.Where("name = ?", "Tom").First(&u6) // SELECT * FROM `users` WHERE name = 'Tom' ORDER BY `users`.`id` LIMIT 1 var u66 User db.Where(&User{Name: "Tom"}).First(&u66) // 相同功能的写法,传入 User 结构体。 var u666 User db.Where(map[string]interface{}{"Name": "Tome"}).First(&u666) // 相同功能的写法,传入 map。
// 获取全部的匹配的记录 var u7 []User db.Where("name <> ?", "Tom").Find(&u7) // 查询全部匹配的记录,应该传入一个数组。如果传入一个结构体变量,只会保存第一条记录。 // SELECT * FROM `users` WHERE name <> 'Tom'
// IN var u8 []User db.Where("name IN ?", []string{"Tom", "Bob"}).Find(&u8) // SELECT * FROM `users` WHERE name IN ('Tom','Bob')
// LIKE var u9 []User db.Where("name LIKE ?", "%Bob%").Find(&u9) // SELECT * FROM `users` WHERE name LIKE '%Bob%'
// AND var u10 User db.Where("name = ? AND age = ?", "Tom", 18).First(&u10) // SELECT * FROM `users` WHERE name = 'Tom' AND age = '18' ORDER BY `users`.`id` LIMIT 1
// Time var u11 []User db.Where("updated_at > ?", "2000-01-01 00:00:00").Find(&u11) // SELECT * FROM `users` WHERE updated_at > '2000-01-01 00:00:00'
// Between var u12 []User db.Where("age BETWEEN ? AND ?", 10, 20).Find(&u12) // SELECT * FROM `users` WHERE age BETWEEN 10 AND 20
// or 条件, 三种写法:string、struct、map var u13 []User db.Where("name = ?", "Tom").Or("name = ?", "Bob").Find(&u13) // SELECT * FROM `users` WHERE name = 'Tom' OR name = 'Bob'
var u133 []User db.Where(User{Name: "Tom"}).Or(User{Name: "Bob"}).Find(&u133) // SELECT * FROM `users` WHERE `users`.`name` = 'Tom' OR `users`.`name` = 'Bob'
var u1333 []User db.Where(map[string]interface{}{"name": "Tom"}).Or(map[string]interface{}{"name": "Bob"}).Find(&u1333) // SELECT * FROM `users` WHERE name = 'Tom' OR name = 'Bob'
/* *【2.字段名是数据库中的名称,不是 struct 的结构体变量名】。 */ type User struct { MyName string`gorm:"column:name"`// 使用 tag 设置数据库中列名为 name } // db.Where("MyName = ?", "Tom").First(&u6) // 错误!无法找到 my_name 这个列 db.Where("name = ?", "Tom").First(&u6) // 正确!使用 数据库列名称
/* *【3.struct 查询会忽略零值,string 和 map 的方式查询不会忽略零值】。 */ var u6 User db.Where("name = ?", "").First(&u6) // string 方式,不会忽略零值 // SELECT * FROM `users` WHERE name = '' ORDER BY `users`.`id` LIMIT 1
var u66 User db.Where(&User{Name: ""}).First(&u66) // struct 式,忽略零值 // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
var u666 User db.Where(map[string]interface{}{"Name": ""}).First(&u666) // map 方式,不会忽略零值 // SELECT * FROM `users` WHERE `Name` = '' ORDER BY `users`.`id` LIMIT 1
// 根据条件选择值,更新 db.Model(&User{}).Where("name = ? ", "Tom").Update("name", "Tom_002") // UPDATE `users` SET `name`='Tom_002',`updated_at`='2022-03-23 00:04:09.841' WHERE name = 'Tom'
// 根据 id 选择值,更新 db.Frist(&u) // 先查询 db.Model(&u).Update("name", "Tom_002") // 再更改 // UPDATE `users` SET `name`='Tom_002',`updated_at`='2022-03-23 00:08:09.696' WHERE `id` = 1
// 根据 id 和条件选择值,更新 db.Model(&u).Where("Age > ?", 20).Update("name", "Tom_002") // UPDATE `users` SET `name`='Tom_002',`updated_at`='2022-03-23 00:08:40.8' WHERE Age > 20 AND `id` = 1
5.3 更新多个字段
Updates 不能更新非零值。原因在「更新零值问题」中阐述过。
1 2 3 4 5 6 7 8 9 10
var u User db.First(&u)
// 根据 strut 更新 db.Model(&u).Updates(User{Name: "Tom", Age: 10}) // UPDATE `users` SET `name`='Tom',`age`=10,`updated_at`='2022-03-23 00:19:02.321' WHERE `id` = 1
// 根据 map 更新 db.Model(&u).Updates(map[string]interface{}{"name": "Tom", "age": 10}) // UPDATE `users` SET `age`=10,`name`='Tom',`updated_at`='2022-03-23 00:19:02.406' WHERE `id` = 1
5.4 更新选定的字段
select:只更新 Select 选中的字段【可以更新零值字段】
Omit:忽略选中的字段
1 2
db.Model(&u).Select("name", "age").Updates(User{Name: "Tom", Age: 0}) // UPDATE `users` SET `name`='Tom',`age`=0,`updated_at`='2022-03-23 00:26:06.779' WHERE `id` = 1
func(u *User) BeforeUpdate(tx *gorm.DB)(err error) { if u.Age < 18 { return errors.New("be Not allowed to update") } return }
尝试更新,返回值得到预期的错误。
1 2 3 4
var u User db.First(&u) result := db.Model(&u).Updates(User{Name: "Tom", Age: 10}) fmt.Println(result.Error, result.RowsAffected) // be Not allowed to update 0
5.6 批量更新
如果不通过 Model 指定记录的主键,则 GORM 会执行批量更新。
1 2 3 4 5 6 7 8
// 根据 struct 更新 db.Model(&User{}).Where("name = ?", "Tom").Updates(User{Name: "Tom_001", Age: 28}) // UPDATE `users` SET `name`='Tom_001',`age`=28,`updated_at`='2022-03-23 00:44:12.596' WHERE name = 'Tom'
// 根据 map 更新 db.Model(&User{}).Where("name = ?", "Tom").Updates(map[string]interface{}{"name": "Tom_001", "age": 28}) // UPDATE `users` SET `age`=28,`name`='Tom_001',`updated_at`='2022-03-23 00:45:26.133' WHERE name = 'Tom'
一个 User 实体属于 Company 实体,那么外键的名字一般使用 CompanyID,代码可读性更好。
也可以自定义外键名字的方式,需要使用标签指明。
1 2 3 4 5 6 7 8 9 10 11
type User struct { gorm.Model Name string CompanyRefer int Company Company `gorm:"foreignKey:CompanyRefer"`// 指明使用 CompanyRefer 作为外键。 }
type Company struct { ID int Name string }
7.1.4 关联查询
虽然 User 结构体中嵌套了 Company 结构体,但是查询时,只会 select Users 表,需要关联查询才能查到 Company 的信息。
1 2 3 4
var u User db.First(&u) // 只会 select Users 表 // SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1 fmt.Println(u.Company.Name)
var u User db.Preload("Company").First(&u) // 两次查询 // SELECT * FROM `companies` WHERE `companies`.`id` = 1 // SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1 fmt.Println(u.Company.Name)
var u User db.Joins("Company").First(&u) // 一次查询 // SELECT `users`.`id`,`users`.`created_at`,`users`.`updated_at`,`users`.`deleted_at`,`users`.`name`,`users`.`company_refer`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` `Company` ON `users`.`company_refer` = `Company`.`id` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1 fmt.Println(u.Company.Name)
比如每个 user 可以会多门 language,一门 language 也可以被多个 user 使用。
7.3.1 创建关联表
使用标签指定 many to many 的关系,并会自动创建连接表,连接表中有两个外键,分别是 User 和 Language。
1 2 3 4 5 6 7 8 9 10 11 12
// User 拥有并属于多种 language,`user_languages` 是连接表 type User struct { gorm.Model Languages []Language `gorm:"many2many:user_languages;"`// 指定 many to many 的关系 }
type Language struct { gorm.Model Name string }
db.AutoMigrate(&User{}, &Language{}) // 自动创建 many to many 的连接表。
7.3.2 关联插入
1 2 3 4 5 6 7 8 9 10 11 12
// 按照 User 结构体的逻辑创建即可。 db.Create(&User{ Languages: []Language{ {Name: "golang"}, {Name: "java"}, }, }) // 先创建 user 记录,再创建 language 记录,最后创建 连接表 记录。
// INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`) VALUES ('2022-04-02 02:13:47.389','2022-04-02 02:13:47.389',NULL) RETURNING `id` // INSERT INTO `languages` (`created_at`,`updated_at`,`deleted_at`,`name`) VALUES ('2022-04-02 02:13:47.423','2022-04-02 02:13:47.423',NULL,'golang'),('2022-04-02 02:13:47.423','2022-04-02 02:13:47.423',NULL,'java') ON DUPLICATE KEY UPDATE `id`=`id` RETURNING `id` // INSERT INTO `user_languages` (`user_id`,`language_id`) VALUES (3,6),(3,7) ON DUPLICATE KEY UPDATE `user_id`=`user_id`
7.3.3 关联查询
和 Has many 一样,使用 Preload。
1 2 3 4 5 6 7 8 9 10
var u User db.Preload("Languages").First(&u) for _, language := range u.Languages { fmt.Println(language.Name) }
// 会查三张表。 // SELECT * FROM `user_languages` WHERE `user_languages`.`user_id` = 1 // SELECT * FROM `languages` WHERE `languages`.`id` IN (1,2) AND `languages`.`deleted_at` IS NULL // SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1
// 查看 u 的关联数据 languages。 var l []Language db.Model(&u).Association("Languages").Find(&l) for _, language := range l { fmt.Println(language.Name) } //SELECT `languages`.`id`,`languages`.`created_at`,`languages`.`updated_at`,`languages`.`deleted_at`,`languages`.`name` FROM `languages` JOIN `user_languages` ON `user_languages`.`language_id` = `languages`.`id` AND `user_languages`.`user_id` = 1 WHERE `languages`.`deleted_at` IS NULL
7.4.2 添加关联
已经查到 user,需要新增该 user 的全部 language 数据。
1 2 3
var u User db.First(&u) db.Model(&u).Association("Languages").Append([]Language{{Name: "python"}, {Name: "html"}})