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)
...
}
网友评论