Gorm for SQL Join 关联查询

Gorm for SQL Join 关联查询

Belongs To: http://gorm.io/docs/belongs_to.html
Has One: http://gorm.io/docs/has_one.html
Has Many: http://gorm.io/docs/has_many.html
Many To Many: http://gorm.io/docs/many_to_many.html
Associations: http://gorm.io/docs/associations.html
Preloading (Eager Loading): http://gorm.io/docs/preload.html

参考官方文档关于 Associations 的部分,连表关系分成 Belongs To、 Has One、 Has Many、 Many To Many、 Associations 几种。

定义了一个 User 和 Company, User 中可以包含多个 Company, 如下:

type User struct {
    ID        int        `gorm:"TYPE:int(11); NOT NULL; PRIMARY_KEY; INDEX"`
    Name      string     `gorm:"TYPE: VARCHAR(255); DEFAULT:'';INDEX"`
    Companies []Company  `gorm:"FOREIGNKEY:UserId; ASSOCIATION_FOREIGNKEY:ID"`
}

type Company struct {
    gorm.Model
    Industry int    `gorm:"TYPE:INT(11);DEFAULT:0"`
    Name     string `gorm:"TYPE:VARCHAR(255);DEFAULT:'';INDEX"`
    Job      string `gorm:"TYPE:VARCHAR(255);DEFAULT:''"`
    UserId   int    `gorm:"TYPE:int(11);NOT NULL;INDEX"`
}

ASSOCIATION_FOREIGNKEY 指定本主表字段名作为关联外键,此关联外键字段与 FOREIGNKEY 指定的外键在连表查询时进行匹配。在查询 User 时希望把 Company 的信息也一并查询, 基本操作通过 Related()、Association()、Preload() 三种方法实现:

  • 使用 Related 方法, 需要把把 User 查询好, 然后根据 User 中定义的 FOREIGNKEY 指定外键字段名去查找 Company 表中 UserId 匹配的记录, 如果没定义, 则调用 Related(value, foreignKeys) 时需要指定。注意 Related() 和 Find() 一样时立即方法,即会请求数据,如果又添加 Find(&u.Companies) 就是 Multiple Immediate Methods 方式,可能出现逻辑错误,参考 Method Chaining 链式操作。

      var u User
      db.First(&u)
      db.Model(&u).Related(&u.Companies)
    
  • 使用 Association 方法, 需要把把 User 查询好, 然后根据 User 定义中指定的 AssociationForeignKey 去查找 Company, 必须定义。

      var u User
      db.First(&u)
      db.Model(&u).Association("Companies").Find(&u.Companies)
    
  • 使用 Preload 方法, 在查询 User 时先去获取 Company 的记录。

      // 查询单条 user
      var u User
      db.Debug().Preload("Companies").First(&u)
    
      // 对应的 sql 语句
      // SELECT * FROM users LIMIT 1;
      // SELECT * FROM companies WHERE user_id IN (1);
       
      // 查询所有 user
      var users []User
      db.Debug().Preload("Companies").Find(&users)
    
      // 对应的 sql 语句
      // SELECT * FROM users;
      // SELECT * FROM companies WHERE user_id IN (1,2,3...);
    

Find(&user) 这类方法会返回完整的表字段,包含 select 没有指定的字段置 0 或空字符串,或 Unit 起始时间戳。因为 &user 本身是一个 stuct ,肯定是一个完整的结构,没有值的字段会有默认值,如果不想显示那些的话,可以使用 Scan/ScanRows 等方法:

type Result struct {
  Name string
  Age  int
}

var result Result
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)

// Raw SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)

扫描 sql.Rows 数据到模型

rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()

for rows.Next() {
  var user User
  // ScanRows 扫描一行到 user 模型
  db.ScanRows(rows, &user)

  // do something
}

使用 *sql.Row.Scan() 或者 *sql.Rows.Scan() 填充查询结果

row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
row.Scan(&name, &age)

rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
    ...
    rows.Scan(&name, &age, &email)
    ...
}

// 原生SQL
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
    ...
    rows.Scan(&name, &age, &email)
    ...
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容