Golang SQL rows.Scan函数用于通用类型的所有字段。

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

Golang SQL rows.Scan function for all fields of generic type

问题

我想使用sql包中的Scan()函数来执行一个可能(或可能不)返回多行的select语句,并在我的函数中返回这些结果。

我对Golang的泛型还不熟悉,对如何实现这一点感到困惑。
通常,我们会在*sql.Rows上使用Scan函数,并提供我们期望的“结果类型”的所有字段的引用,例如:

var alb Album
rows.Scan(&alb.ID, &alb.Title, &alb.Artist,
            &alb.Price, &alb.Quantity)

其中Album是一个具有这五个字段的结构体类型。

现在,为了不为我拥有的每个SQL表编写类似的函数N次,我想使用一个泛型类型R。R是泛型接口类型Result,并且我将将此类型定义为N个不同结构体之一:

type Result interface {
	StructA | StructB | StructC
}

func ExecSelect[R Result](conn *sql.DB, cmd Command, template R) []R

现在,我如何编写rows.Scan(...)以将Scan操作应用于R结构体的所有字段?例如,我希望有rows.Scan(&res.Field1, &res.Field2, ...),其中res的类型为R,并且Scan应接收我当前具体类型R的所有字段。我是否实际上需要提供一个R具体类型的“模板”作为参数,以便在运行时清楚哪个结构体现在是相关的?

请纠正我在泛型方面可能犯的任何错误。

英文:

I want to use the Scan() function from the sql package for executing a select statement that might (or not) return multiple rows, and return these results in my function.

I´m new to Golang generics, and am confused about how to achieve this.
Usually, we would use the Scan function on a *sql.Rows and provide the references to all fields of our expected 'result type' we want to read the rows into, e.g.:

var alb Album
rows.Scan(&alb.ID, &alb.Title, &alb.Artist,
            &alb.Price, &alb.Quantity)

where Album is a struct type with those five fields shown.

Now, for the purpose of not writing a similar function N times for every SQL table I have, I want to use a generic type R instead. R is of generic interface type Result, and I will define this type as one of N different structs:

type Result interface {
	StructA | StructB | StructC
}

func ExecSelect[R Result](conn *sql.DB, cmd Command, template R) []R

How can I now write rows.Scan(...) to apply the Scan operation on all fields of my struct of R´s concrete type? e.g. I would want to have rows.Scan(&res.Field1, &res.Field2, ...) where res is of type R, and Scan should receive all fields of my current concrete type R. And do I actually need to provide a 'template' as argument of R´s concrete type, so that at runtime it becomes clear which struct is now relevant?

Please correct me on any mistake I´m making considering the generics.

答案1

得分: 2

这是一个不适合使用泛型的糟糕案例。

sql.Rows.Scan 函数的参数应该是扫描目标,也就是你的结构体字段,每个字段对应结果集中的一列。在泛型函数体内,你无法访问 R 类型参数的字段。

即使你可以访问到字段,你的 Result 约束中的结构体可能有不同的字段...那么你如何设想编写适用于不同字段的通用代码呢?

你可以使用像 sqlx 这样的包来实现你想要的功能,它提供了类似 StructScan 的任意结构体扫描功能,但它在内部使用反射将结构体字段映射到 sql.Rows.Scan 的参数中,所以你并没有从泛型中获得任何好处。

如果有的话,情况可能会更糟,因为现在你还要承担使用类型参数的额外性能开销。

英文:

This is a poor use case for generics.

The arguments to the function sql.Rows.Scan are supposed to be the scan destinations, i.e. your struct fields, one for each column in the result set, and within the generic function body you do not have access to the fields of R type parameter.

Even if you did, the structs in your Result constraint likely have different fields...? So how do you envision writing generic code that works with different fields?

You might accomplish what you want with a package that provides arbitrary struct scanning like sqlx with facilities like StructScan, but that uses reflection under the hood to map the struct fields into sql.Rows.Scan arguments, so you are not getting any benefit at all with generics.

If anything, you are making it worse, because now you have the additional performance overheads of using type parameters.

huangapple
  • 本文由 发表于 2022年4月19日 17:07:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/71922375.html
匿名

发表评论

匿名网友

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

确定