介绍
Gorm是一个迭代了十几年的ORM框架,对go开发者十分友好
Gorm是通过驱动的方式连接数据库,目前支持MySQL,SQLServer,PostgreSQL,SQLite和TiDB 如果需要连接其它类型的数据库,可以复用/自行开发驱动。
安装
在go mod的相同目录下打开终端,输入
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
这样就导入Gorm依赖以及Gorm支持MySQL的驱动
简单入门
模型
Gorm是通过将Go结构体映射成数据库表来简化数据库交互的。因此需要学习如何在Gorm中定义Model,才能充分利用Gorm的功能
模型定义
模型是使用普通结构体定义的。这些结构体可以包含具有基本Go类型,指针,或这些类型的别名。 自定义类型也是可以的,只需要实现database/sql包中的Scanner和Valuer接口
对于下面这个User模型
type User struct {
ID uint // Standard field for the primary key
Name string // A regular string field
Email *string // A pointer to a string, allowing for null values
Age uint8 // An unsigned 8-bit integer
Birthday *time.Time // A pointer to time.Time, can be null
MemberNumber sql.NullString // Uses sql.NullString to handle nullable strings
ActivatedAt sql.NullTime // Uses sql.NullTime for nullable time fields
CreatedAt time.Time // Automatically managed by GORM for creation time
UpdatedAt time.Time // Automatically managed by GORM for update time
ignored string // fields that aren't exported are ignored
}
- 具体基本类型如uint,string,uint8直接使用
- 指针类型
*string,*time.Time表示可空字段 database/sql包的sql.NullString和sql.NullTime用于具有更多控制的可空字段。-
CreatedAt和UpdatedAt是特殊字段,当记录被创建或更新时,GORM 会自动向内填充当前时间。
- 小写字母开头的字段名,就是非导出字段,不会被映射
约定
- Gorm默认使用名为ID的字段作为每个模型的主键
- 默认情况下,Gorm使用结构体的蛇形复数形式作为表名
- 自动使用字段名的蛇形作为列名
- 使用
CreatedAt``UpdatedAt字段自动跟踪记录创建和更新时间 遵循这些约定有利于提升开发效率。但是Gorm也支持自定义这些配置。这里不过多介绍
gorm.Model
这是Gorm自带的结构体,名为gorm.Model
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
- 可以直接在我们自定义的结构体中嵌入这个类型,会自动包含这些字段。有助于在不同的模型间保持一致性,遵循Gorm的约定
- 其中的
DeletedAt字段用于软删除(将记录标记为已删除,实际在数据库仍然存在,只是打了个标记,就是懒标记吧)
连接到数据库
这里介绍最常用的MySQL的连接方式
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
创建记录,插入记录
创建记录
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user) // 通过数据的指针来创建
user.ID // 返回插入数据的主键
result.Error // 返回 error
result.RowsAffected // 返回插入记录的条数
使用Create()方法插入记录,传递的是一个数据的指针,就插入一个。 如果要一次性创建多条记录,就要传递一个指针数组,切片进去
注意,你无法向Create传递结构体,所以你应该传递一个数据的指针
users := []*User{
{Name: "Jinzhu", Age: 18, Birthday: time.Now()},
{Name: "Jackson", Age: 19, Birthday: time.Now()},
}
result := db.Create(users)
用指定的字段创建记录
创建记录,并为指定字段赋值
db.Select("Name", "Age", "CreatedAt").Create(&user)
创建记录,并且忽略传递给Omit的字段值
db.Omit("Name", "Age", "CreatedAt").Create(&user)
批量插入
要高效地插入大量记录,就用上面的传递一个切片给Create方法。Gorm将生成一条SQL来插入所有数据并返回所有主键值,并触发Hook方法。 当这些记录可以被分割成多个批次,Gorm会开启一个事务来处理它们。
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)
for _, user := range users {
user.ID // 1,2,3
}
通过db.CreateInBatches方法来指定批量插入的批次大小
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// batch size 100
db.CreateInBatches(users, 100)
简单查询
检索单个对象
Gorm提供了几个方法用于从数据库中查找单个对象,当查询数据库时添加了LIMIT 1条件,且没找到记录,会返回ErrRecordNotFound错误
获取第一条记录(主键升序)
db.First(&user) // SELECT * FROM users ORDER BY id LIMIT 1;
获取一条记录,没有指定排序字段
db.Take(&user) // SELECT * FROM users LIMIT 1;
获取最后一条记录(主键降序)
db.Last(&user) // SELECT * FROM users ORDER BY id DESC LIMIT 1;
result := db.First(&user)
result.RowsAffected // 返回找到的记录数
result.Error // returns error or nil
检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)
根据主键检索
如果主键是数字类型,您可以使用内联条件 来检索对象。
db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;
db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;
db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);
检索全部对象
// Get all records
result := db.Find(&users)
// SELECT * FROM users;
result.RowsAffected // returns found records count, equals `len(users)`
result.Error // returns error
更新
保持所有字段
Save 会保存所有的字段,即使字段是零值
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)|
保存 是一个组合函数。 如果保存值不包含主键,它将执行 Create,否则它将执行 Update (包含所有字段)。
更新单列
当使用 Update 更新单列时,需要有一些条件,否则将会引起ErrMissingWhereClause 错误
当使用 Model 方法,并且它有主键值时,主键将会被用于构建条件
// 根据条件更新
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
// User 的 ID 是 `111`db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// 根据条件和 model 的值进行更新
db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
更新多列
Updates 方法支持 struct 和 map[string]interface{} 参数。当使用 struct 更新时,默认情况下GORM 只会更新非零值的字段
// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
// 根据 `map` 更新属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
更新选定字段
如果要在更新时选择、忽略某些字段,可以使用 Select、Omit
当然可以,下面是按照您的要求将代码块拆分,并将注释转换为文字说明后的结果:
选择 Map 的字段
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
当 User 的 ID 是 111 时,只更新 name 字段,其他字段即使在 map 中存在也不会被更新。这将执行如下 SQL 语句:
UPDATE users SET name='hello' WHERE id=111;
忽略特定字段进行更新
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
这个示例忽略了 name 字段,因此不会更新该字段,而只会更新 age 和 active 字段。假设 updated_at 是一个自动更新的时间戳字段,那么执行的 SQL 可能如下所示:
UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
选择 Struct 的字段(包括零值字段)
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
此示例指定了要更新的字段 Name 和 Age,即使 Age 的值为零也会被更新。这将执行以下 SQL 语句:
UPDATE users SET name='new_name', age=0 WHERE id=111;
选择所有字段(包括零值字段)
db.Model(&user).Select("*").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})
使用 * 表示选择所有字段进行更新,这意味着即使是零值字段也会被包含在内。根据提供的结构体实例,SQL 语句将会是:
UPDATE users SET name='jinzhu', role='admin', age=0 WHERE id=111;
选择除特定字段外的所有字段(包括零值字段)
db.Model(&user).Select("*").Omit("Role").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})
这个例子中,我们选择了除了 Role 字段之外的所有字段进行更新。即使提供了 Role 字段的值,它也不会被更新。SQL 语句可能看起来像这样:
UPDATE users SET name='jinzhu', age=0 WHERE id=111;
删除
删除一条记录
删除一条记录时,删除对象需要指定主键,否则会触发 批量删除
// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10;
// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";
根据主键删除
GORM 允许通过主键(可以是复合主键)和内联条件来删除对象
db.Delete(&User{}, 10)
// DELETE FROM users WHERE id = 10;
db.Delete(&User{}, "10")
// DELETE FROM users WHERE id = 10;
db.Delete(&users, []int{1,2,3})
// DELETE FROM users WHERE id IN (1,2,3);