gorm 实战-Association | 青训营

276 阅读3分钟

我们接上篇文章,地址gorm 实战 | 青训营 - 掘金 (juejin.cn)

上篇文章主要业务场景为:短视频点关注操作

下面,我们进行查询操作,业务描述如下:

用户登录:
成功 ——> 获取用户关注列表 | 获取用户粉丝列表

回顾之前model模型

  1. model
type Model struct {  
ID int64 `json:"id" gorm:"primarykey;comment:主键"`  
CreatedAt time.Time `json:"-" gorm:"comment:创建时间"`  
UpdatedAt time.Time `json:"-" gorm:"comment:修改时间"`  
DeletedAt gorm.DeletedAt `json:"-" gorm:"comment:删除时间"`  
}
  1. user
type (  
// User 用户信息表  
User struct {  
Model  
Name string `json:"name" gorm:"index:,unique;size:32;comment:用户名称"`  
Pawd string `json:"-" gorm:"size:128;comment:用户密码"`  
Avatar string `json:"avatar" gorm:"comment:用户头像"`  
BackgroundImage string `json:"background_image" gorm:"comment:用户个人页顶部大图"`  
Signature string `json:"signature" gorm:"default:此人巨懒;comment:个人简介"`  
WorkCount int64 `json:"work_count" gorm:"default:0;comment:作品数量"`  
Follow []*User `json:"follow,omitempty" gorm:"many2many:UserFollow;comment:关注列表"`  
}
)

我们还是使用单数命名表策略,策略使用只需在连接数据库时候,在&gorm.Config{},中将SingularTable: true

db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{  
NamingStrategy: schema.NamingStrategy{  
SingularTable: true, //表名以单数形式命名  
},  
TranslateError: true, // 启用错误翻译功能  
})

ok!!!,会在数据库中创建两个表,我介绍下,首先一定有user表,其次就是一个中间表user_follow,中间表user_follow会有两个字段,一个为user_id,一个为follow_id,这两个字段都是外键,都引用user表的id字段,同时又是联合主键

CREATE TABLE user_follow ( user_id bigint NOT NULL COMMENT '主键',
follow_id bigint NOT NULL COMMENT '主键',
PRIMARY KEY (user_id,follow_id),
KEY fk_user_follow_follow (follow_id),
CONSTRAINT fk_user_follow_follow FOREIGN KEY (follow_id) REFERENCES user (id),
CONSTRAINT fk_user_follow_user FOREIGN KEY (user_id) REFERENCES user (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

ok!!!,我下面来介绍获取用户关注列表的实现:

// RelationFollowGet 获取关注列表 uid:本人id tid:待查id  
func RelationFollowGet(uid, tid int64) ([]*model.User, error) {  
var data []*model.User  
err := db.Set("user_id", uid).Model(&model.User{Model: id(tid)}).Association("Follow").Find(&data)  
if err != nil {  
return nil, err  
}  
return data, nil  
}

执行的底层sql大致如下:
SELECT user.* FROM user JOIN user_follow ON user_follow.follow_id = user.id AND user_follow.user_id = ? WHERE user.deleted_at IS NULL;

获取用户粉丝列表
这里我们不能使用Association,Association 是获取指定模型关联的数据,我们这里是给定关联数据,即用户id(谁关注该用户),来反向获取id

// RelationFollowerGet 获取粉丝列表 uid:本人id tid:待查id  
func RelationFollowerGet(uid, tid int64) ([]*model.User, error) {  
var data []*model.User  
err := db.Set("user_id", uid).Table("user").  
Joins("JOIN user_follow ON `user`.`id` = `user_follow`.`user_id` AND `user_follow`.`follow_id` = ?", tid).  
Select("`user`.*").Find(&data).Error  
if err != nil {  
return nil, err  
}  
return data, nil  
}

执行sql一眼就知:
SELECT user.* FROM user JOIN user_follow ON user.id = user_follow.user_id AND user_follow.follow_id = ? WHERE user.deleted_at IS NULL;

Association在GORM中是一个重要的概念,它表示模型之间的关联关系。

更详细地介绍Association:

  1. 表示关系

Association表示数据库表之间的关系,如一对一、一对多和多对多等关系。

  1. 类型
  • BelongsTo:一对一,参照型,Inverse 位于belongs to一方
  • HasOne:一对一,拥有型
  • HasMany:一对多
  • ManyToMany:多对多
  1. 定义

通过模型字段定义Association关系,如:

type Order struct {
  Product Product `gorm:"association_autoupdate:false"`
}
  1. 查询

通过Association进行关联查询,如Preload预加载避免N+1。

  1. 操作

如Append/Delete对关联记录执行操作。

  1. 定制

通过Query等定制关联查询条件。

  1. 更新

根据Inverse或own自身更新关联记录。

  1. Constraints

设置外键等约束。

  1. 逆向引用

通过BelongsTo.AssociationGetField访问逆向引用。

  1. 节省内存

通过RowsBackedAssociation避免重复读取关联记录。

Association利用关系对数据库操作进行抽象,大大简化了关系型数据库的使用难度。深入了解它可以更高效使用GORM开发项目。