Golang Gorm从v1升级到v2后,作用域(scope)出现问题。

huangapple go评论79阅读模式
英文:

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{})

huangapple
  • 本文由 发表于 2022年6月18日 12:47:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/72666748.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定