解决Gorm中使用Count后关联查询失效的问题
问题描述
当我们 在go中使用gorm进行多表join关联查询的时候
如果还有分页的需求
那么可能会是这样写
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
ormLogger "gorm.io/gorm/logger"
"time"
)
func main() {
type Detail struct {
UesrId int `json:"user_id"` // 自增 id
Age int `json:"age"` // 年龄
Email string `json:"email"` // 邮箱
}
type User struct {
Id int `json:"id" gorm:"primaryKey"` // 自增 id
Name string `json:"name"` // 名字
Detail `gorm:"foreignKey:UesrId"`
}
type MysqlConfig struct {
MysqlUrl string
Logger ormLogger.Writer
MaxIdleCount int
MaxOpen int
MaxLifetime time.Duration
LogLevel ormLogger.LogLevel
}
var c MysqlConfig
DB, err := gorm.Open(mysql.Open(c.MysqlUrl))
if err != nil {
panic("GORM 连接失败," + err.Error())
}
tx := DB.Model(&User{}).Joins("Detail")
var count int64
tx.Count(&count)
var data []User
tx.Limit(GetLimit()).Offset(GetOffset()).Find(&data)
}
这样count会计算出值, 而 再查询数据就 会出现数据为空的情况
问题分析:
通过查询Count方法的源码我们发现
tx.Statement.AddClause(clause.Select{Expression: clause.Expr{SQL: "count(*)"}})
这里如果调用count方法,gorm会把你的sql的select的字段转换成 count* 所以,通过join关联查询的方式不可以进行对应字段的映射了
解决思路
我们执行查询和执行记数的tx,使用两个就好了 因为go语言是引用类型传递,所以该怎么进行拷贝tx对象呢
查询gorm相关源码发现,session()的源码里包含
// Session create new db session
func (db *DB) Session(config *Session) *DB {
var (
txConfig = *db.Config
tx = &DB{
Config: &txConfig,
Statement: db.Statement,
Error: db.Error,
clone: 1,
}
)
if config.Context != nil || config.PrepareStmt || config.SkipHooks {
tx.Statement = tx.Statement.clone()
tx.Statement.DB = tx
}
func (stmt *Statement) clone() *Statement {
copy(newStmt.Joins, stmt.Joins)
...
// 在这里执行了copy方法
}
所以我们可以利用gorm中的session功能深拷贝一个 tx对象,即:tx2 := tx.session()
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
ormLogger "gorm.io/gorm/logger"
"time"
)
func main() {
type Detail struct {
UesrId int `json:"user_id"` // 自增 id
Age int `json:"age"` // 年龄
Email string `json:"email"` // 邮箱
}
type User struct {
Id int `json:"id" gorm:"primaryKey"` // 自增 id
Name string `json:"name"` // 名字
Detail `gorm:"foreignKey:UesrId"`
}
type MysqlConfig struct {
MysqlUrl string
Logger ormLogger.Writer
MaxIdleCount int
MaxOpen int
MaxLifetime time.Duration
LogLevel ormLogger.LogLevel
}
var c MysqlConfig
DB, err := gorm.Open(mysql.Open(c.MysqlUrl))
if err != nil {
panic("GORM 连接失败," + err.Error())
}
tx := DB.Model(&User{}).Joins("Detail")
tx2 := tx.Session(&gorm.Session{})
var count int64
tx2.Count(&count)
var data []User
tx.Limit(GetLimit()).Offset(GetOffset()).Find(&data)
}
这样通过gorm中的session可以深拷贝出一个 gorm对象 执行不会受影响