一.简介与数据介绍
1. GORM
首先了解ORM:Object-Relationl Mapping,即对象关系映射,这里的Relationl指的是关系型数据库。
它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。Gorm框架能够通过Go语言结构体来定义数据库模型,它自动处理了数据库表和结构体之间的映射,允许通过操作结构体来进行数据查询、插入、更新和删除等操作,而无需直接编写SQL语句。GORM框架的特点:
- 1.全功能ORM;
- 2.关联(包含一个,包含多个,属于,多对多,多种包含);
- 3.Callbacks(创建/保存/更新/删除/查找之前/之后),后面会提到相应的钩子函数;
- 4.预加载;
- 5.事务;
- 6.自动迁移;
- 7.日志;
- 8.可扩展,编写基于GORM回调的插件;
2. Model结构定义
GORM通过定义结构体来定义GORM Model,同时我们需要实现TableName接口来帮助GORM找到数据库中对应表。否则,表名应按照GORM默认定义为结构体名+s,不然会报错找不到对应表。本文中我们使用User结构,定义如下:
type User struct {
Id int64 `gorm:"column:id"`
Name string `gorm:"column:name"`
Avatar string `gorm:"column:avatar"`
Level int `gorm:"column:level"`
CreateTime time.Time `gorm:"column:create_time"`
ModifyTime time.Time `gorm:"column:modify_time"`
}
func (User) TableName() string {
return "user"
}
在定义Gorm model时可以使用各种标签(tags)和方法来为模型添加不同的操作和功能:
- 表名和列名映射:使用gorm:"column:name"标签来映射模型字段到数据库表的列名;
- 主键设置:使用gorm:"primaryKey"标签来指定主键字段;
- 自增字段:使用gorm:"autoIncrement"标签来指定自增字段;
- 唯一索引:使用gorm:"unique"标签来为字段添加唯一索引;
- 默认值:使用gorm:"default:value"标签来为字段指定默认值;
- 关联关系:使用gorm:"foreignKey"、gorm:"references"和其他关联关系标签来定义模型之间的关联关系;
- 自定义方法:你可以在模型上定义自己的方法,用于执行特定的数据库操作或业务逻辑;
- 查询作用域:使用scope方法创建查询作用域,以便在查询中复用特定的条件;
- 忽略字段:使用gorm:"-"标签来忽略模型中的某些字段,使其不参与数据库操作;
- 表名设置:使用gorm:"tableName"标签来指定数据库表的名称;
- 软删除设置:使用Deleted gorm.DeletedAt标志字段,表示对该模型进行删除操作时,不直接进行物理删除,删除时并不会从数据库中移除数据记录,而是在记录中添加一个标志字段(deleted_at),表示该记录已被删除,在查询时一般的查询语句会直接忽略软删除的数据进行查询,同时也可以使用Unscoped不忽略软删除数据进行查询;
- 结构体嵌套:通过在模型中嵌套其他结构体来表示更复杂的数据库关系。
二. 数据操作增删改查
1.数据查询
常用到以下函数:
Find函数:用于查询满足条件的多条数据记录,查询不到数据不返回错误;First函数:用于查询满足条件的第一条数据记录,查询不到数据则返回ErrRecordNotFound容易造成线上问题,换用where,未找到返回空数组;Take函数:用于随机获取一条数据记录;Where函数:用于添加条件查询;Order函数:用于指定查询结果的排序方式;Limit函数:用于限制查询结果的数量;Offset函数:用于设置查询结果的偏移量;Select函数:用于选择要查询的字段;Joins函数:用于关联查询。
注意:当使用结构体查询时,GORM只会对非零值进行查询。若需要查询零值需要使用map来构造。
下面的代码中使用了几种不同的查询模式,包括多条数据查询、复合查询等:
func (*UserDao) QueryUserById(id int64) (*User, error) {
var user User
err := db.Where("id = ?", id).Find(&user).Error
if err == gorm.ErrRecordNotFound {
return nil, nil
}
if err != nil {
util.Logger.Error("find user by id err:" + err.Error())
return nil, err
}
return &user, nil
}
var users []*User
err := db.Where("id in (?)", ids).Find(&users).Error
db.Where("name IN ?", []string{"Jerry", "Tom"}).Find(&users)
db.Where("name=? AND id>=?", "Jerry", "2").Find(&users)
db.Where(&User{Name: "Jerry", Id: 2}).Find(&users)
db.Where(map[string]interface{}{"name": "Jerry", "id": 2}).Find(&users)
简单实例:
2.数据创建
常用函数:
Create函数:用于创建新的数据记录,需要使用指针传递;Save函数:用于创建或更新数据记录,根据主键(或唯一索引)是否存在来决定操作;FirstOrCreate函数:查找第一条满足条件的记录,如果不存在则创建;
其中:Save函数当主键不存在时进行Insert,而存在时则进行Update.
func (*UserDao) CreateUser(user *User) error {
if err := db.Create(user).Error; err != nil {
util.Logger.Error("insert post err:" + err.Error())
return err
}
return nil
}
user := &repository.User{
Id: 3,
Name: "Jame",
Level: 3,
CreateTime: time.Now(),
ModifyTime: time.Now(),
}
error := userDao.CreateUser(user)
3.数据删除
Deleted gorm.DeletedAt标志字段,设置模型删除模式为软删除;Delete函数:用于删除数据记录;Where函数 +Delete函数:使用Where函数指定删除的条件,然后调用Delete函数删除数据;Unscoped函数 +Delete函数:使用Unscoped函数删除包括软删除在内的记录;Model函数 +Where函数 +Delete函数:通过Model函数指定模型和条件,然后使用Delete函数删除数据。
4.数据更新
Model函数 +Update函数:通过Model函数指定表名和条件,然后使用Update函数更新单列数据;Updates函数:更新多列数据,使用结构体更新时只会更新非零值,如果要更新零值需要使用map传递或者用Select选择字段;Save函数:用于创建或更新数据记录,根据主键(或唯一索引)是否存在来决定操作;UpdatesColumns函数:用于更新指定列的数据记录。
更新的过程中可以用到Where等函数以增加条件,注意此处上课强调,必须在Update函数之前增加条件,否则更新已经执行,条件无效。
db.Model(&User{Id: id}).Where("name=?", "Tom").Update("name", "Jame")
5.事务
Begin函数:用于开始一个事务;Commit函数:用于提交事务,将操作永久保存到数据库;Rollback函数:用于回滚事务,取消之前的操作;Transaction函数:执行事务操作。在这个函数中执行数据库操作,并根据每个操作的错误情况来决定是否提交事务或回滚事务。如果返回的错误为 nil,事务将被提交;如果返回的错误不为 nil,事务将被回滚,用于自动提交事务,避免漏写Commit和Rollback。 通过db.Transaction函数实现事务,如果闭包函数返回错误,则回滚事务:
db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,应该使用 'tx' 而非 'db')
if err := tx.Create(&User{Name: "Lili"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&User{Name: "xiaoming"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
6.钩子函数
Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。一般命名为BeforeCreate、AfterUpdate,可进行如验证、审计、日志记录、触发器等操作。
func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
if u.Id == 1 {
return errors.New("无法删除Jerry")
}
return nil
}