英文:
Golang Gorm scope broken after upgrade from v1 to v2
问题
我曾经使用 Gorm v1。我有一个用于分页的作用域,之前是正常工作的:
func Paginate(entity BaseEntity, p *Pagination) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
var totalRows int64
db.Model(entity).Count(&totalRows)
totalPages := int(math.Ceil(float64(totalRows) / float64(p.PerPage)))
p.TotalPages = totalPages
p.TotalCount = int(totalRows)
p.SetLinks(entity.ResourceName())
return db.Offset(p.Offset).Limit(p.PerPage)
}
}
我调用它的方式是:
if err := gs.db.Scopes(entities.Paginate(genre, p)).Find(&gs.Genres).Error; err != nil {
return errors.New(err.Error())
}
再次强调,这在 Gorm v1 中是正常工作的,直到我升级到 Gorm v2。现在我得到了以下错误信息:
[0.204ms] [rows:2] SELECT * FROM
genres
LIMIT 2
sql: expected 9 destination arguments in Scan, not 1; sql: expected 9 destination arguments in Scan, not 1[GIN] 2022/06/18 - 00:41:00 | 400 | 1.5205ms | 127.0.0.1 | GET "/api/v1/genres"
Error #01: sql: expected 9 destination arguments in Scan, not 1; sql: expected 9 destination arguments in Scan, not 1
我发现错误是由于这一行引起的:
db.Model(entity).Count(&totalRows)
因为如果我删除它,那么查询将被正确执行(显然,TotalPages
的数据是不正确的,因为它没有被计算)。在查看文档时,我看到了 https://gorm.io/docs/method_chaining.html#Multiple-Immediate-Methods,所以我猜测获取 totalRows
的连接被重用,并且有一些残留数据,因此我的偏移和限制查询失败了。
我尝试为计数和偏移查询创建一个新的会话:
db.Model(entity).Count(&totalRows).Session(&gorm.Session{})
return db.Offset(p.Offset).Limit(p.PerPage).Session(&gorm.Session{})
希望每个查询都使用自己的会话,但似乎不起作用。
有什么建议吗?
英文:
I was using Gorm v1. I had this scope for pagination which was working correctly:
func Paginate(entity BaseEntity, p *Pagination) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
var totalRows int64
db.Model(entity).Count(&totalRows)
totalPages := int(math.Ceil(float64(totalRows) / float64(p.PerPage)))
p.TotalPages = totalPages
p.TotalCount = int(totalRows)
p.SetLinks(entity.ResourceName())
return db.Offset(p.Offset).Limit(p.PerPage)
}
}
and the way I called it:
if err := gs.db.Scopes(entities.Paginate(genre, p)).Find(&gs.Genres).Error; err != nil {
return errors.New(err.Error())
}
again, this used to work correctly, that is until I upgraded to Gorm v2. Now I'm getting the following message:
> [0.204ms] [rows:2] SELECT * FROM genres
LIMIT 2
sql: expected 9 destination arguments in Scan, not 1; sql: expected 9 destination arguments in Scan, not 1[GIN] 2022/06/18 - 00:41:00 | 400 | 1.5205ms | 127.0.0.1 | GET "/api/v1/genres"
Error #01: sql: expected 9 destination arguments in Scan, not 1; sql: expected 9 destination arguments in Scan, not 1
Now, I found out that the error is due to this line:
db.Model(entity).Count(&totalRows)
because if I remove it then my query is being correctly executed (obviously the data for TotalPages
is not correct since it wasn't calculated). Going through the documentation I saw https://gorm.io/docs/method_chaining.html#Multiple-Immediate-Methods so my guess is that the connection used to get totalRows
is being reused and have some residual data therefore my offset and limit query is failing.
I tried to create a new session for both the count and the offset queries:
db.Model(entity).Count(&totalRows).Session(&gorm.Session{})
return db.Offset(p.Offset).Limit(p.PerPage).Session(&gorm.Session{})
hoping that each one will use their own session but doesn't seem to work.
Any suggestions?
答案1
得分: 1
如果有人需要的话:
我确实需要创建一个新的会话,但我没有以正确的方式创建它。最终我这样做了:
countDBSession := db.Session(&gorm.Session{Initialized: true})
countDBSession.Model(entity).Count(&totalRows)
这样做是有效的。所以我的范围现在是:
// Paginate是一个Gorm范围函数。
func Paginate(entity BaseEntity, p *Pagination) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
var totalRows int64
// 我们必须创建一个新的会话来运行计数,否则使用相同的db连接
// 我们将得到一些残留数据,这将导致db.Offset(p.Offset).Limit(p.PerPage)失败。
countDBSession := db.Session(&gorm.Session{Initialized: true})
countDBSession.Model(entity).Count(&totalRows)
totalPages := int(math.Ceil(float64(totalRows) / float64(p.PerPage)))
p.TotalPages = totalPages
p.TotalCount = int(totalRows)
p.SetLinks(entity.ResourceName())
return db.Offset(p.Offset).Limit(p.PerPage)
}
}
请注意,我使用一个新的会话countDBSession
来获取计数,这不会影响后面对*db.Gorm
参数的使用return db.Offset(p.Offset).Limit(p.PerPage).Session(&gorm.Session{})
。
英文:
In case anyone needs it:
I did have to create a new session but I wasn't creating it the right way. I ended up doing:
countDBSession := db.Session(&gorm.Session{Initialized: true})
countDBSession.Model(entity).Count(&totalRows)
and that worked as expecting. So my scope now is:
// Paginate is a Gorm scope function.
func Paginate(entity BaseEntity, p *Pagination) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
var totalRows int64
// we must create a new session to run the count, otherwise by using the same db connection
// we'll get some residual data which will cause db.Offset(p.Offset).Limit(p.PerPage) to fail.
countDBSession := db.Session(&gorm.Session{Initialized: true})
countDBSession.Model(entity).Count(&totalRows)
totalPages := int(math.Ceil(float64(totalRows) / float64(p.PerPage)))
p.TotalPages = totalPages
p.TotalCount = int(totalRows)
p.SetLinks(entity.ResourceName())
return db.Offset(p.Offset).Limit(p.PerPage)
}
}
notice that I'm using a new session to get the count via countDBSession
which won't affect the latter use of the *db.Gorm
parameter in return db.Offset(p.Offset).Limit(p.PerPage).Session(&gorm.Session{})
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论