// sqlite go get -u gorm.io/driver/sqlite // mysql go get -u gorm.io/driver/mysql // sql server go get -u gorm.io/driver/sqlserver // pgsql go get -u gorm.io/driver/postgres
err = db.AutoMigrate(User{}) if err != nil { log.Fatal(err) }
可以看到日志如下,默认情况,在没有指定表名时,表名为全小写,驼峰体变更为下划线。
1 2 3 4
[1.399ms] [rows:-] SELECT DATABASE() [6.693ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'gorm%' ORDER BY SCHEMA_NAME='gorm' DESC,SCHEMA_NAME limit 1 [6.108ms] [rows:-] SELECT count(*) FROM information_schema.tables WHERE table_schema = 'gorm' AND table_name = 'users' AND table_type = 'BASE TABLE' [34.257ms] [rows:0] CREATE TABLE `users` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime NULL,`updated_at` datetime NULL,`deleted_at` datetime NULL,`name` varchar(256),PRIMARY KEY (`id`),INDEX `idx_users_deleted_at` (`deleted_at`))
其中,gorm.Model 的定义
1 2 3 4 5 6
type Model struct { ID uint`gorm:"primaryKey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` }
一般通过结构体嵌套使用
1 2 3 4 5 6 7 8 9 10 11 12
type User struct { gorm.Model Name string } // 等效于 type User struct { ID uint`gorm:"primaryKey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` Name string }
测试结构体
1 2 3 4 5 6 7 8 9 10 11
type User struct { ID uint// 默认情况下,会将id字段设置为主键 Name string Email *string Age uint8 Birthday *time.Time MemberNumber sql.NullString ActivatedAt sql.NullTime CreatedAt time.Time UpdatedAt time.Time }
CRUDL
新增
1 2 3 4
result = db.Create(&User{ // 传入指针,创建后,该类型的id会被更新 Name: "mitaka", }) // [14.754ms] [rows:1] INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`) VALUES ('2022-09-26 15:33:49.137','2022-09-26 15:33:49.137',NULL,'mitaka')
db.First(&User{}, 1) // 根据主键查询 db.First(&User{}, "name = ?", "mitaka") // 根据条件查询 // [2.951ms] [rows:1] SELECT * FROM `users` WHERE `users`.`id` = 1 AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1 // [2.618ms] [rows:1] SELECT * FROM `users` WHERE name = 'mitaka' AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1
// 根据主键查询多行 var users []User db.Find(&users, []int{1, 2, 3}) // [5.704ms] [rows:3] SELECT * FROM `users` WHERE `users`.`id` IN (1,2,3)
// 如果主键是字符串(例如像 uuid),查询将被写成这样:(ps:string类型的主键支持这种方式查询是新版功能,老版本会出现只截取整数类型的数值) var user User db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a") // [15.420ms] [rows:1] SELECT * FROM `users` WHERE id = '1b74413f-f3b8-409f-ac47-e8c062e3472a' ORDER BY `users`.`id` LIMIT 1
条件查询
1 2 3 4 5 6 7
// Get all records result := db.Find(&users) // SELECT * FROM users;
// 返回值 result.RowsAffected // returns found records count, equals `len(users)` result.Error // returns error
使用string的方式,(当没有设置column的tag时,搜索的字段名,大小写不敏感)
1 2 3 4
// 使用string更新 var users []User db.Where("name = ?", "mitaka").Find(&users) // 通过users,获取到表名 // [5.784ms] [rows:1] SELECT * FROM `users` WHERE name = 'mitaka'
// Get all matched records db.Where("name <> ?", "jinzhu").Find(&users) // SELECT * FROM users WHERE name <> 'jinzhu';
// IN db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users) // SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
// LIKE db.Where("name LIKE ?", "%jin%").Find(&users) // SELECT * FROM users WHERE name LIKE '%jin%';
// AND db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users) // SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
// Time db.Where("updated_at > ?", lastWeek).Find(&users) // SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
// BETWEEN db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users) // SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
ps:使用结构体作为查询条件时,依然有零值问题。
更多的查询方式和条件使用,详见官方文档:查询和高级查询
更新
根据主键更新一个
1 2 3
var u User db.First(&u, 1) // 根据主键查询 db.Model(&u).Update("name", "xiaoyeshiyu")
db.Model(&User{}).Where(User{Name: "xiaoyeshiyu"}).Update("name", "mitaka") // [5.208ms] [rows:0] UPDATE `users` SET `name`='mitaka',`updated_at`='2022-09-26 15:33:49.157' WHERE `users`.`name` = 'xiaoyeshiyu' AND `users`.`deleted_at` IS NULL
type User struct { gorm.Model Name string Age int }
1 2
db.Model(&u).Updates(User{Name: "mitaka", Age: 0}) // [13.125ms] [rows:0] UPDATE `users` SET `updated_at`='2022-09-26 15:49:08.27',`name`='mitaka' WHERE `users`.`deleted_at` IS NULL AND `id` = 1
可以看到,没有更新Age的语法,此时可以通过map[string]interface{}更新
1 2 3 4 5
db.Model(&u).Updates(map[string]interface{}{ "name": "mitaka", "age": 0, }) // [5.152ms] [rows:0] UPDATE `users` SET `age`=0,`name`='mitaka',`updated_at`='2022-09-26 15:49:37.67' WHERE `users`.`deleted_at` IS NULL AND `id` = 1
也可以使用Select强制更新(使用select时,需要指定所有需要更新的字段)
1 2
db.Model(&u).Select("name", "age").Updates(User{Name: "mitaka", Age: 0}) // [5.838ms] [rows:0] UPDATE `users` SET `updated_at`='2022-09-26 15:51:47.907',`name`='mitaka',`age`=0 WHERE `users`.`deleted_at` IS NULL AND `id` = 1
还可以通过sql中的NullInt64类型实现
1 2 3 4 5
type User struct { gorm.Model Name string Age sql.NullInt64 }
1 2 3 4 5
db.Model(&u).Updates(User{Name: "mitaka", Age: sql.NullInt64{ Int64: 0, Valid: true, // true为更新,false不更新 }}) // [4.523ms] [rows:0] UPDATE `users` SET `updated_at`='2022-09-26 15:56:51.534',`name`='mitaka',`age`=0 WHERE `users`.`deleted_at` IS NULL AND `id` = 1
另外,还可以将字段设置为指针类型,也可以更新零值
1 2 3 4
var empty string user := User{Name: "mitaka", Email: &empty} db.Model(User{ID: 1}).Updates(&user) // [16.577ms] [rows:1] UPDATE `users` SET `name`='mitaka',`email`='',`updated_at`='2022-09-26 17:24:38.672' WHERE `id` = 1
通过save保存所有字段(包括零值),save是一个创建和更新语句
1 2 3 4 5 6 7 8 9 10 11
// 没有主键 var user User user.Name = "mitaka" user.Age = 28 db.Save(&user) // [15.861ms] [rows:1] INSERT INTO `users` (`name`,`email`,`age`,`birthday`,`member_number`,`activated_at`,`created_at`,`updated_at`) VALUES ('mitaka',NULL,28,NULL,NULL,NULL,'2022-09-26 17:13:36.619','2022-09-26 17:13:36.619')
// 有主键 user.ID = 2 db.Save(&user) // [12.707ms] [rows:1] UPDATE `users` SET `name`='mitaka',`email`=NULL,`age`=28,`birthday`=NULL,`member_number`=NULL,`activated_at`=NULL,`created_at`='2022-09-26 17:15:03.18',`updated_at`='2022-09-26 17:15:03.196' WHERE `id` = 2
删除
根据主键删除(不带主键的话,会触发批量删除)
1 2
db.Delete(&u, 1) // [10.681ms] [rows:1] UPDATE `users` SET `deleted_at`='2022-09-26 15:37:42.275' WHERE `users`.`id` = 1 AND `users`.`id` = 1 AND `users`.`deleted_at` IS NULL
不带主键,带条件的批量删除
1 2
db.Where(User{Name: "mitaka"}).Delete(&User{}) // [8.132ms] [rows:1] UPDATE `users` SET `deleted_at`='2022-09-26 15:40:25.535' WHERE `users`.`name` = 'mitaka' AND `users`.`deleted_at` IS NULL
auto increment step, controls the interval between successive column values,自增增量
embedded
embed the field,嵌入的结构体
embeddedPrefix
column name prefix for embedded fields,嵌入结构体的前缀
autoCreateTime
track current time when creating, for int fields, it will track unix seconds, use value nano/milli to track unix nano/milli seconds, e.g: autoCreateTime:nano,自动创建时间,如果是int类型,则是跟踪unix秒
autoUpdateTime
track current time when creating/updating, for int fields, it will track unix seconds, use value nano/milli to track unix nano/milli seconds, e.g: autoUpdateTime:milli,自动更新时间
index
create index with options, use same name for multiple fields creates composite indexes, refer Indexes for details,索引
set field’s write permission, <-:create create-only field, <-:update update-only field, <-:false no write permission, <- create and update permission,设置写入和更新权限
->
set field’s read permission, ->:false no read permission,设置读取权限
-
ignore this field, - no read/write permission, -:migration no migrate permission, -:all no read/write/migrate permission,忽略字段
comment
add comment for field when migration,添加注释
多个标签,通过;隔开
1 2 3 4 5
type User struct { gorm.Model Name string`gorm:"column:my_name;type:varchar(50)"` Age sql.NullInt64 }
多表操作
多表操作适用于需要表关联的场景,例如belongs to (一对一属于,能有多个A属于一个B),has one(一对一,一个A只有一个B),has many (一对多,一个A拥有多个B),Many to Many(多对多,一个A属于多个B,一个B有多个A)。
belongs to
1 2 3 4 5 6 7 8 9 10 11 12
// `User` 属于 `Company`,`CompanyID` 是外键 type User struct { gorm.Model Name string CompanyID int Company Company }
type Company struct { ID int Name string }
创建表
1 2
// 同时新建users表和companies表 db.AutoMigrate(User{})
创建时,则需要确保外键存在,因此,创建时,会优先创建Company,再创建User
1 2 3 4 5 6 7 8 9
user := User{ Name: "mitaka", Company: Company{ Name: "x", }, } db.Create(&user) // [13.027ms] [rows:1] INSERT INTO `companies` (`name`) VALUES ('x') ON DUPLICATE KEY UPDATE `id`=`id` // [25.259ms] [rows:1] INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`company_id`) VALUES ('2022-09-26 17:45:32.484','2022-09-26 17:45:32.484',NULL,'mitaka',1)
当外键存在时,可以直接通过id指定
1 2 3 4 5 6
user := User{ Name: "mitaka01", CompanyID: 1, } db.Create(&user) // [9.861ms] [rows:1] INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`company_id`) VALUES ('2022-09-26 17:47:17.531','2022-09-26 17:47:17.531',NULL,'mitaka01',1)
关联查询
1 2 3 4
var user User db.Preload("Company").Find(&user, 1) // 放入关联的字段名称 // [4.732ms] [rows:1] SELECT * FROM `companies` WHERE `companies`.`id` = 1 // [10.439ms] [rows:1] SELECT * FROM `users` WHERE `users`.`id` = 1 AND `users`.`deleted_at` IS NULL
或者通过join方法,相比之下,join是使用的一条语句,使用join操作
1 2
db.Joins("Company").Find(&user, 1) // [4.426ms] [rows:1] SELECT `users`.`id`,`users`.`created_at`,`users`.`updated_at`,`users`.`deleted_at`,`users`.`name`,`users`.`company_id`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` `Company` ON `users`.`company_id` = `Company`.`id` WHERE `users`.`id` = 1 AND `users`.`deleted_at` IS NULL
u1 := User{ Languages: []Language{language1, language2}, } u2 := User{ Languages: []Language{language3, language2}, } db.Create(&u1) // [3.321ms] [rows:2] INSERT INTO `languages` (`created_at`,`updated_at`,`deleted_at`,`name`) VALUES ('2022-09-27 09:38:15.442','2022-09-27 09:38:15.442',NULL,'go'),('2022-09-27 09:38:15.442','2022-09-27 09:38:15.442',NULL,'python') ON DUPLICATE KEY UPDATE `id`=`id` // [4.177ms] [rows:2] INSERT INTO `user_languages` (`user_id`,`language_id`) VALUES (1,1),(1,2) ON DUPLICATE KEY UPDATE `user_id`=`user_id` // [20.401ms] [rows:1] INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`) VALUES ('2022-09-27 09:38:15.435','2022-09-27 09:38:15.435',NULL) db.Create(&u2) // [2.758ms] [rows:2] INSERT INTO `languages` (`created_at`,`updated_at`,`deleted_at`,`name`) VALUES ('2022-09-27 09:38:15.458','2022-09-27 09:38:15.458',NULL,'php'),('2022-09-27 09:38:15.458','2022-09-27 09:38:15.458',NULL,'python') ON DUPLICATE KEY UPDATE `id`=`id` // [3.019ms] [rows:2] INSERT INTO `user_languages` (`user_id`,`language_id`) VALUES (2,3),(2,4) ON DUPLICATE KEY UPDATE `user_id`=`user_id` // [14.102ms] [rows:1] INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`) VALUES ('2022-09-27 09:38:15.456','2022-09-27 09:38:15.456',NULL)
获取
1 2 3 4 5 6 7 8
u := User{} db.Preload("Languages").Find(&u, 1) // [4.432ms] [rows:2] SELECT * FROM `user_languages` WHERE `user_languages`.`user_id` = 1 // [4.026ms] [rows:2] SELECT * FROM `languages` WHERE `languages`.`id` IN (1,2) AND `languages`.`deleted_at` IS NULL // [13.775ms] [rows:1] SELECT * FROM `users` WHERE `users`.`id` = 1 AND `users`.`deleted_at` IS NULL for _, l := range u.Languages { fmt.Println(l.Name) }
反向引用
1 2 3 4 5 6 7 8 9 10
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 u User u.ID = 1 var l []*Language db.Model(&u).Association("Languages").Find(&l) // [6.577ms] [rows:2] 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