原文链接:
1.(九)GORM 关联查询-属于 https://www.cnblogs.com/infodriven/p/16351582.html
2.(十)GORM 关联查询 - 一对一关系(has one) https://www.cnblogs.com/infodriven/p/16351597.html
3.(十一)GORM 关联查询 - 一对多关系(Has Many) https://www.cnblogs.com/infodriven/p/16351606.html
4.(十二)GORM 关联查询预加载 Preloading https://www.cnblogs.com/infodriven/p/16351613.html

1.外键连表-ForeignKey

GORM的关联查询(又叫连表查询)中的属于关系是一对一关联关系的一种,通常用于描述一个Model属于另外一个Model。
存在一个users表和profiles表:

  • users - 用户表
  • profiles - 用户个性化信息表
  • // 他们之间存在一对一关系,每一个用户都有自己的个性化数据,那么可以说每一条profiles记录都属于某个用户。
    // 用户表 - 下面使用go struct表示表结构
    type User struct {
      // 继承gorm的基础Model,里面默认定义了ID、CreatedAt、UpdatedAt、DeletedAt 4个字段
      gorm.Model
      Name string
    // 个性化信息表
    type Profile struct {
      gorm.Model
      UserID uint // 外键
      // 定义user属性关联users表,默认情况使用 类型名 + ID 组成外键名,在这里UserID属性就是外键
      User   User
      Name   string
    

    1.1 外键

    在关联查询中必须包含外键,默认gorm使用(关联属性类型 + 主键)组成外键名,如上面的例子User + ID 组成UserID,UserID就作为Profile的外键。
    也可以通过下面方式修改外键

    type Profile struct {
      gorm.Model
      Name      string
      User      User `gorm:"foreignkey:UserRefer"` //使用 UserRefer 作为外键
      UserRefer uint // 外键
    

    1.2 关联外键

    在连表操作中,除了外键,还需要一个关联外键组成一对才能完成连表,例如上面的例子,Profile中UserID属性作为外键,它和User中的ID进行关联,这里User的ID就是关联外键。
    默认GORM使用主键作为关联外键,所以上面的User使用ID作为关联外键。
    也可以自定义关联外键

    type User struct {
      gorm.Model
      Refer string // 关联外键
      Name string
    type Profile struct {
      gorm.Model
      Name      string
      User      User `gorm:"references:Refer"` // 使用 Refer 作为关联外键
      UserRefer string
    

    1.3 属于关联查询示例

    profile := Profile{}
    // 查询用户个性数据
    //自动生成sql: SELECT * FROM `profiles` WHERE id = 1 AND `profiles`.`deleted_at` IS NULL LIMIT 1
    db.Where("id = ?", 1).Take(&profile)
    fmt.Println(profile)
    user := User{}
    // 通过Profile关联查询user数据, 查询结果保存到user变量
    db.Model(&profile).Association("User").Find(&user)
    fmt.Println(user)
    // 自动生成sql: SELECT * FROM `users` WHERE `users`.`id` = 1 // 1 就是user的 ID,已经自动关联
    

    2.一对一关联关系

    GORM的关联查询(又叫连表查询)中的Has One关系是一对一关联关系的一种,通常用于描述一个Model拥有另外一个Model。

    提示:Has one很像属于(belongs to)关系,都是一对一关系,区别是Has One关系和属于关系,持有关联Model属性的关系是相反的,例如:A 关联 B,Has One关系通常是A 结构体持有B属性, belongs to关系则是B结构体持有A

    每一个用户都有一张信用卡,下面以Go Struct表示表结构

    // 信用卡
    type CreditCard struct {
      // 继承gorm的基础Model,里面默认定义了ID、CreatedAt、UpdatedAt、DeletedAt 4个字段
      gorm.Model
      Number   string
      UserID   uint // 外键
    // 用户
    type User struct {
      gorm.Model
      CreditCard   CreditCard // 持有信用卡属性(关联信用卡)
    

    2.1 外键

    关联查询必须包含外键,默认情况下Has One关系的外键由持有关联属性的类型名 + 主键 组成外键名,如上例,User关联CreditCard的外键就是User + ID = UserID。

    通过下面方式可以自定义外键

    type CreditCard struct {
      gorm.Model
      Number   string
      UserName string // 外键
    type User struct {
      gorm.Model
      // 通过标签将外键定义为:UserName
      CreditCard CreditCard `gorm:"foreignkey:UserName"`
    

    2.2 关联外键

    默认情况下,保存User的时候,会自动将User的主键保存到外键UserID中,关联查询的时候,也会使用外键和关联外键进行关联进行查询,这里User的ID就是关联外键。

    自定义关联外键的例子

    type CreditCard struct {
      gorm.Model
      Number string
      UID    string
    type User struct {
      gorm.Model
      Name       `sql:"index"` // 关联外键
      // 自定义关联外键为:name
      CreditCard CreditCard `gorm:"foreignkey:uid;references:name"`
    

    2.3 关联查询例子

    user := User{}
    // 查询用户数据
    //自动生成sql: SELECT * FROM `users`  WHERE (username = 'tizi365') LIMIT 1
    db.Where("username = ?", "tizi365").First(&user)
    fmt.Println(user)
    var card CreditCard
    ////自动生成SQL: SELECT * FROM credit_cards WHERE user_id = 123; // 123 自动从user的ID读取
    // 关联查询的结果会填充到card变量
    db.Model(&user).Association("CreditCard").Find(&card)
    

    3.一对多关联关系

    GORM的关联查询(又叫连表查询)中的Has Many关系是一对多关联关系,通常用于描述一个Model拥有多个Model。
    一个用户拥有多张信用卡,下面以Go Struct表示表结构

    // 用户
    type User struct {
      // 继承gorm的基础Model,里面默认定义了ID、CreatedAt、UpdatedAt、DeletedAt 4个字段
      gorm.Model
      CreditCards []CreditCard // 一对多关联属性,表示多张信用卡
    // 信用卡
    type CreditCard struct {
      gorm.Model
      Number   string // 卡号
      UserID  uint // 默认外键, 用户Id
    

    3.1 外键

    默认情况下,GORM使用持有关联属性的 类型名 + 主键ID 作为外键名。
    如上例,User使用User + ID = UserID 作为外键名。
    自定义外键

    type User struct {
      gorm.Model
      // 通过标签,将外键定义为:UserRefer
      CreditCards []CreditCard `gorm:"foreignkey:UserRefer"`
    type CreditCard struct {
      gorm.Model
      Number    string
      UserRefer uint // 新定义的外键名
    

    3.2 关联外键

    外键和关联外键都是成对出现的,默认情况GORM使用主键ID,作为关联外键。
    主键ID,默认为ID,如上面的例子,使用User的ID作为关联外键
    自定义关联外键

    type User struct {
      gorm.Model
      MemberNumber string // 关联外键字段
      // 使用references定义关联外键名
      CreditCards  []CreditCard `gorm:"foreignkey:UserMemberNumber;references:MemberNumber"`
    type CreditCard struct {
      gorm.Model
      Number           string
      UserMemberNumber string // 外键字段
    

    3.3 一对多关联查询例子

    user := User{}
    // 查询用户数据
    //自动生成sql: SELECT * FROM `users`  WHERE (username = 'tizi365') LIMIT 1
    db.Where("username = ?", "tizi365").First(&user)
    fmt.Println(user)
    //自动生成SQL: SELECT * FROM emails WHERE user_id = 111; // 111 是user的主键ID值
    // 关联查询的结果,保存到user.CreditCard属性
    db.Model(&user).Association("CreditCard").Find(&user.CreditCard)
    

    4.预加载关联数据

    默认情况下GORM因为性能问题,不会自动加载关联属性的值,gorm通过Preload函数支持预加载(Eager loading)关联数据。

    4.1 预加载例子

    // 用户表
    type User struct {
      gorm.Model
      Username string
      Orders []Orders // 关联订单,一对多关联关系
    // 订单表
    type Orders struct {
      gorm.Model
      UserID uint // 外键字段 
      Price float64
    // 预加载Orders字段值,Orders字段是User的关联字段
    db.Preload("Orders").Find(&users)
    // 下面是自动生成的SQL,自动完成关联查询
    //// SELECT * FROM users;
    //// SELECT * FROM orders WHERE user_id IN (1,2,3,4);
    // Preload第2,3个参数支持设置SQL语句条件和绑定参数
    db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
    // 自动生成的SQL如下
    //// SELECT * FROM users;
    //// SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');
    // 通过组合Where函数一起设置SQL条件
    db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
    // 自动生成的SQL如下
    //// SELECT * FROM users WHERE state = 'active';
    //// SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');
    // 预加载Orders、Profile、Role多个关联属性
    // ps: 预加载字段,必须是User的属性
    db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
    //// SELECT * FROM users;
    //// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
    //// SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
    //// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to
    

    4.2 自动预加载

    type User struct {
      gorm.Model
      Name       string
      CompanyID  uint
      Company    Company `gorm:"PRELOAD:false"` // 通过标签属性关闭预加载
      Role       Role                           // 默认开启预加载特性
    // 通过Set设置gorm:auto_preload属性,开启自动预加载,查询的时候才会自动完成关联查询
    db.Set("gorm:auto_preload", true).Find(&users)
    

    4.3 嵌套预加载

    // 预加载User.Orders.OrderItems属性值,使用点连接嵌套属性即可
    db.Preload("Orders.OrderItems").Find(&users)
    db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)