推荐阅读官方教程
安装
和 gin 一样, 使用前需要先使用以下命令安装:
go get -u gorm.io/gorm
本篇以 MySQL 举例, 所以还需要安装 MySQL 的驱动:
go get -u gorm.io/driver/mysql
连接
连接数据库只需要一句话即可:
dsn := "user:password@tcp(127.0.0.1:3306)/db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
建表
连接以后可以使用 AutoMigrate
创建数据表:
db.AutoMigrate(&User{}) // User是一个结构体
User
定义如下:
type User struct {
gorm.Model
Name string
Age int `gorm:"default:12"` // 表示默认值为12
Salary float32
Birthday time.Time
}
gorm.Model
是 gorm 官方模型, 其定义如下:
type Model struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt DeletedAt `gorm:"index"`
}
gorm.Model 中的值会在修改数据库时自动填充
gorm 默认表名为结构体的蛇形复数, 字段名为蛇形命名, id 为自增主键, 比如上述模型的数据表为:
你也可以自定义表名:
type Tabler interface {
TableName() string
}
// TableName 会将 User 的表名重写为 `profiles`
func (User) TableName() string {
return "profiles"
}
除了上面展示的 default
标签, 你也可以自定义以下标签:
增删改查
增加
gorm 可以单个增加:
user := User{Name: "张三", Age: 13, Salary: 12.0, Birthday: time.Now()}
db.Debug().Create(&user) // 打印sql语句
也可以增加多个:
users := make([]User, 0)
users = append(users, User{Name: "张三", Salary: 12.0, Birthday: time.Now()})
users = append(users, User{Name: "张三", Salary: 15.0, Birthday: time.Now()})
users = append(users, User{Name: "李四", Salary: 15.0, Birthday: time.Now()})
db.Debug().Create(&users)
但是注意: 当结构体中字段值为空或者零时, 此时插入数据库则会填入默认值
user := User{Name: "张三", Age: 0, Salary: 12.0, Birthday: time.Now()}
db.Debug().Create(&user)
比如上述代码打印的 sql 语句如下:
可以看到图中插入的 sql 语句在 Age
处的值为默认值 12, 并不是我们想要的 0, 针对此问题我们有两种方法解决:
-
使用指针类型变量
将字段类型改为指针, 结构体定义如下:
type User struct { gorm.Model Name string Age *int `gorm:"default:12"` Salary float32 Birthday time.Time }
go 语句如下:
// 指针类型的零值需要使用 new() user := User{Name: "张三", Age: new(int), Salary: 12.0, Birthday: time.Now()} db.Debug().Create(&user)
sql 语句如下:
可以看到
Age
字段的值变为了 0 -
使用 Scanner/Valuer 接口
结构体定义如下:
type User struct { gorm.Model Name string Age sql.NullInt32 `gorm:"default:12"` Salary float32 Birthday time.Time }
可以查看
sql.NullInt32
的定义如下, 可以知道这是一个结构体, 当Valid
为true
时, 使用Int32
的值; 否则认为传入的值为空, 使用默认值// NullInt32 represents an int32 that may be null. // NullInt32 implements the Scanner interface so // it can be used as a scan destination, similar to NullString. type NullInt32 struct { Int32 int32 Valid bool // Valid is true if Int32 is not NULL } // Scan implements the Scanner interface. func (n *NullInt32) Scan(value any) error { if value == nil { n.Int32, n.Valid = 0, false return nil } n.Valid = true return convertAssign(&n.Int32, value) } // Value implements the driver Valuer interface. func (n NullInt32) Value() (driver.Value, error) { if !n.Valid { return nil, nil } return int64(n.Int32), nil }
go 语句如下:
user := User{Name: "张三", Age: sql.NullInt32{0, true}, Salary: 12.0, Birthday: time.Now()} db.Debug().Create(&user)
sql 语句如下:
删除
当你的结构体定义中有 go.Model
时, 此时你将获取软删除功能, 你的删除操作都是将 delete_at
设定为删除事件, 当你执行查询操作时会默认加上一个 delete_al is null
的条件
// 删除单个 user的id作为条件
// UPDATE users SET deleted_at="2023-07-01 17:03" WHERE id = 67;
db.Delete(&user)
// 批量删除
// UPDATE users SET deleted_at="2023-07-01 17:13" WHERE age = 15;
db.Where("age = ?", 15).Delete(&User{})
// 查询记录时会忽略被软删除的记录
// SELECT * FROM users WHERE age = 15 AND deleted_at IS NULL;
db.Where("age = 15").Find(&user)
// Unscoped 方法可以查询被软删除的记录
// SELECT * FROM users WHERE age = 15;
db.Unscoped().Where("age = 15").Find(&users)
// 永久删除 user的id作为条件
// DELETE FROM orders WHERE id=78;
db.Unscoped().Delete(&user)
修改
更新所有字段
db.First(&user)
user.Name = "李四"
*user.Age = 17
// UPDATE `users` SET `created_at`='2023-07-01 17:58:57.788',`updated_at`='2023-07-01 17:58:57.803',`deleted_at`=NULL,`name`='李四',`age`=17,`salary`=12.000000,`birthday`='2023-07-01 17:58:57.788' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Debug().Save(&user)
db.First(&user)
更新部分字段
// 更新单个属性,如果它有变化
// UPDATE `users` SET `name`='赵六',`updated_at`='2023-07-01 17:58:57.805' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Debug().Model(&user).Update("name", "赵六")
// 根据给定的条件更新单个属性
// UPDATE `users` SET `name`='赵六',`updated_at`='2023-07-01 17:58:57.807' WHERE age = 12 AND `users`.`deleted_at` IS NULL AND `id` = 199
db.Debug().Model(&user).Where("age = ?", 12).Update("name", "赵六")
// 使用 map 更新多个属性,只会更新其中有变化的属性
// UPDATE `users` SET `age`=18,`name`='张三',`updated_at`='2023-07-01 17:58:57.808' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Debug().Model(&user).Updates(map[string]interface{}{"name": "张三", "age": 18})
更新选定字段
// UPDATE `users` SET `name`='赵六',`updated_at`='2023-07-01 17:58:57.809' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Debug().Model(&user).Select("name").Updates(map[string]interface{}{"name": "赵六", "age": 17})
// UPDATE `users` SET `age`=17,`updated_at`='2023-07-01 17:58:57.811' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Debug().Model(&user).Omit("name").Updates(map[string]interface{}{"name": "赵六", "age": 17})
批量更新
// UPDATE `users` SET `age`=18,`name`='李' WHERE id IN (151,155)
db.Debug().Table("users").Where("id IN (?)", []int{151, 155}).Updates(map[string]interface{}{"name": "李", "age": 18})
// 使用 struct 更新时,只会更新非零值字段,若想更新所有字段,请使用map[string]interface{}
// UPDATE `users` SET `updated_at`='2023-07-01 17:58:57.816',`name`='赵六' WHERE 1 = 1 AND `users`.`deleted_at` IS NULL
db.Debug().Model(User{}).Where("1 = 1").Updates(User{Name: "赵六"})
// 使用 `RowsAffected` 获取更新记录总数
// UPDATE `users` SET `updated_at`='2023-07-01 17:58:57.818',`name`='赵六' WHERE 1 = 1 AND `users`.`deleted_at` IS NULL
rows := db.Debug().Model(User{}).Where("1 = 1").Updates(User{Name: "赵六"}).RowsAffected
fmt.Println(rows)
查询
查询条件
String 条件
// SELECT * FROM users WHERE name = '张三' ORDER BY id LIMIT 1;
db.Where("name = ?", "张三").First(&user)
// SELECT * FROM users WHERE name = '张三' ORDER BY id;
db.Where("name = ?", "张三").Find(&users)
Struct 和 Map 条件
// Struct
// SELECT * FROM users WHERE name = "张三" AND age = 15 ORDER BY id LIMIT 1;
age := 15
db.Debug().Where(&User{Name: "张三", Age: &age}).First(&user)
// Map
// SELECT * FROM users WHERE name = "张三" AND age = 15;
db.Where(map[string]interface{}{"name": "张三", "age": 15}).Find(&users)
// 主键切片条件
// SELECT * FROM users WHERE id IN (20, 21, 22);
db.Where([]int64{20, 21, 22}).Find(&users)
内联条件
// SELECT * FROM users WHERE name = "李四";
db.Find(&user, "name = ?", "李四")
NOT 条件
// SELECT * FROM users WHERE NOT name = "张三" ORDER BY id LIMIT 1;
db.Not("name = ?", "张三").First(&user)
// Not In
// SELECT * FROM users WHERE name NOT IN ("张三", "李四");
db.Not(map[string]interface{}{"name": []string{"张三", "李四"}}).Find(&users)
OR 条件
// SELECT * FROM users WHERE name = '李四' OR name = '王五';
db.Where("name = ?", "李四").Or("name = ?", "王五").Find(&users)
选取特定字段
// SELECT name, age FROM users;
db.Select("name", "age").Find(&users)
其他还有诸如 limit
, offset
, group
等查询就不一一列举, 详情可以查看官网的 查询
和 高级查询
总结
增加用 Create()
就行, 删除修改和查询实际上都是使用 Where()
加附加条件, 最后再分别调用 Delete(), Update(), Updates(), First(), Find()
执行最终操作即可
程序清单
package main
import (
"fmt"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string
Age *int `gorm:"default:12"`
Salary float32
Birthday time.Time
}
func main() {
// 修改你的 用户名 密码 连接地址 数据库名
dsn := "user:password@tcp(127.0.0.1:3306)/db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&User{})
// 增加
// 单个增加
user := User{Name: "张三", Age: new(int), Salary: 12.0, Birthday: time.Now()}
db.Create(&user)
// 增加多个
users := make([]User, 0)
users = append(users, User{Name: "张三", Salary: 12.0, Birthday: time.Now()})
users = append(users, User{Name: "李四", Salary: 15.0, Birthday: time.Now()})
users = append(users, User{Name: "王五", Salary: 15.0, Birthday: time.Now()})
db.Create(&users)
// 修改
// 更新所有字段
db.First(&user)
user.Name = "李四"
*user.Age = 17
// UPDATE `users` SET `created_at`='2023-07-01 17:58:57.788',`updated_at`='2023-07-01 17:58:57.803',`deleted_at`=NULL,`name`='李四',`age`=17,`salary`=12.000000,`birthday`='2023-07-01 17:58:57.788' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Save(&user)
db.First(&user)
// 更新部分字段
// 更新单个属性,如果它有变化
// UPDATE `users` SET `name`='赵六',`updated_at`='2023-07-01 17:58:57.805' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Model(&user).Update("name", "赵六")
// 根据给定的条件更新单个属性
// UPDATE `users` SET `name`='赵六',`updated_at`='2023-07-01 17:58:57.807' WHERE age = 12 AND `users`.`deleted_at` IS NULL AND `id` = 199
db.Model(&user).Where("age = ?", 12).Update("name", "赵六")
// 使用 map 更新多个属性,只会更新其中有变化的属性
// UPDATE `users` SET `age`=18,`name`='张三',`updated_at`='2023-07-01 17:58:57.808' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Model(&user).Updates(map[string]interface{}{"name": "张三", "age": 18})
// 更新选定字段
// UPDATE `users` SET `name`='赵六',`updated_at`='2023-07-01 17:58:57.809' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "赵六", "age": 17})
// UPDATE `users` SET `age`=17,`updated_at`='2023-07-01 17:58:57.811' WHERE `users`.`deleted_at` IS NULL AND `id` = 199
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "赵六", "age": 17})
// 批量更新
// UPDATE `users` SET `age`=18,`name`='李' WHERE id IN (151,155)
db.Table("users").Where("id IN (?)", []int{151, 155}).Updates(map[string]interface{}{"name": "李", "age": 18})
// 使用 struct 更新时,只会更新非零值字段,若想更新所有字段,请使用map[string]interface{}
// UPDATE `users` SET `updated_at`='2023-07-01 17:58:57.816',`name`='赵六' WHERE 1 = 1 AND `users`.`deleted_at` IS NULL
db.Model(User{}).Where("1 = 1").Updates(User{Name: "赵六"})
// 使用 `RowsAffected` 获取更新记录总数
// UPDATE `users` SET `updated_at`='2023-07-01 17:58:57.818',`name`='赵六' WHERE 1 = 1 AND `users`.`deleted_at` IS NULL
rows := db.Model(User{}).Where("1 = 1").Updates(User{Name: "赵六"}).RowsAffected
fmt.Println(rows)
// 查询
// SELECT * FROM users WHERE name = '张三' ORDER BY id LIMIT 1;
user = User{}
db.Where("name = ?", "张三").First(&user)
// SELECT * FROM users WHERE name = '张三' ORDER BY id;
user = User{}
db.Where("name = ?", "张三").Find(&users)
// Struct
// SELECT * FROM users WHERE name = "张三" AND age = 15 ORDER BY id LIMIT 1;
age := 12
user = User{}
db.Where(&User{Name: "张三", Age: &age}).First(&user)
// Map
// SELECT * FROM users WHERE name = "张三" AND age = 15;
db.Where(map[string]interface{}{"name": "张三", "age": 15}).Find(&users)
// 主键切片条件
// SELECT * FROM users WHERE id IN (20, 21, 22);
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE name = "李四";
user = User{}
db.Find(&user, "name = ?", "李四")
// SELECT * FROM users WHERE NOT name = "张三" ORDER BY id LIMIT 1;
user = User{}
db.Not("name = ?", "张三").First(&user)
// Not In
// SELECT * FROM users WHERE name NOT IN ("张三", "李四");
db.Not(map[string]interface{}{"name": []string{"张三", "李四"}}).Find(&users)
// SELECT * FROM users WHERE name = '张三' OR name = '李四';
db.Where("name = ?", "张三").Or("name = ?", "李四").Find(&users)
// SELECT name, age FROM users;
db.Select("name", "age").Find(&users)
// 删除
// 删除单个 user里的值当作条件
// UPDATE users SET deleted_at="2023-07-01 17:03" WHERE id = 67;
db.Delete(&user)
// 批量删除
// UPDATE users SET deleted_at="2023-07-01 17:13" WHERE age = 15;
db.Where("age = ?", 15).Delete(&User{})
// 查询记录时会忽略被软删除的记录
// SELECT * FROM users WHERE age = 15 AND deleted_at IS NULL;
db.Where("age = 15").Find(&user)
// Unscoped 方法可以查询被软删除的记录
// SELECT * FROM users WHERE age = 15;
db.Unscoped().Where("age = 15").Find(&users)
// 永久删除
// DELETE FROM orders WHERE id=78;
db.Unscoped().Delete(&user)
// 删库跑路
db.Unscoped().Where("1 = 1").Delete(&User{})
}