Combine row.Scan and rows.Scan interfaces in go?

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

Combine row.Scan and rows.Scan interfaces in go?

问题

我有两个针对特定表的查询 - 一个用于检索单个项目,另一个用于返回列表。第一个查询使用 sql.DB.QueryRow,因为它只需要检索一行数据,而第二个查询使用 sql.DB.Query 来返回多个不同的结果。

问题是序列化需要一些工作,我希望通过一个单一的方法来扫描数据库行并将其读入 Go 类型,以减少重复代码。我的代码目前如下所示:

// 一种类型,与其数据库表示形式有很大差异,并且需要一些序列化工作。
type Foo struct {
    Baz *Baz
    Board [8][8]int
}
 
// 根据 id 获取一个 foo
func GetFoo(id int) {
    row := db.QueryRow("select * from foo where id = ?", id)
    // 与下面的代码重复...
    var foo Foo
    row.Scan(&foo.blah, &foo.etc)
    // 进行更多工作以序列化类型...
}
 
// 获取所有的 foo
func GetFooes() {
    rows, err := db.Query("select * from foo")
    for rows.Next() {
        // 与上面的代码重复...
        var foo Foo
        rows.Scan(&foo.blah, &foo.etc)
        // 进行更多工作以序列化类型...
    }
}

然而,组合 row.Scanrows.Scan 有点棘手。我认为可以使用类似以下的方法:

func serializeFoo(scanner sql.Scanner) (*Foo, error) {

}

不过,sql.Scanner 接口只接受单个 (value interface{}),而不是 (...value interface{}) 的列表。

在这里有什么建议吗?另一个解决方案是将单个的 QueryRow 调用转换为 db.Query

英文:

I have two queries against a particular table in Go - one to retrieve a single item and the other to return a list. The first one uses sql.DB.QueryRow because it only needs to retrieve a single row, and the second one uses sql.DB.Query to return a few different results.

The problem is that serialization takes some work and I'd like to DRY it up by having a single method that scans from a database row and reads it into a Go type. My code right now looks like:

// Some type which varies considerably from its DB representation, and takes some effort to serialize.
type Foo struct {
    Baz *Baz
    Board [8][8]int
}
 
// Get one foo by its id
func GetFoo(id int) {
    row := db.QueryRow("select * from foo where id = ?", id)
    // Work that's duplicated from below...
     var foo Foo
    row.Scan(&foo.blah, &foo.etc)
    // Do more work to serialize the type...
}
 
// Get all of the fooes
func GetFooes() {
    rows, err := db.Query("select * from foo")
    for rows.Next() {
        // Work that's duplicated from above...
        var foo Foo
        rows.Scan(&foo.blah, &foo.etc)
        // Do more work to serialize the type...
    }
}

However combining row.Scan and rows.Scan is proving to be a little tricky. I thought I could use something like:

func serializeFoo(scanner sql.Scanner) (*Foo, error) {

}

though sql.Scanner takes a single (value interface{}) and not a list of (...value interface{}).

Any advice here? Another solution would be to convert the single QueryRow call into a db.Query.

答案1

得分: 2

db.QueryRow是一个方便的函数。除非它能节省打字/代码复杂性,否则没有理由使用它。在这种情况下,它并没有节省,所以我建议你只使用db.Query。

更多详情请参考http://golang.org/src/pkg/database/sql/sql.go?s=25740:25802#L966

正如你所提到的,Row和Rows都没有实现Scanner接口。Scanner接口用于可变参数扫描函数的参数。

如果你想要一个允许Row或Rows的参数,你需要自己创建一个接口。例如:

func serializeFoo(scanner interface{Scan(dest ...interface{}) error}) (*Foo, error) {

}
英文:

db.QueryRow is a convenience function. There is no reason to use it unless it will save on typing/code-complexity. In this case, it doesn't so I recommend you just use db.Query.

See http://golang.org/src/pkg/database/sql/sql.go?s=25740:25802#L966 for more details


As you mentioned, neither Row nor Rows implement the Scanner interface. Scanner is used for arguments of the variatic scan functions.

If you want to have a parameter that allows either Row or Rows, you need to make your own interface For example:

func serializeFoo(scanner interface{Scan(dest ...interface{}) error}) (*Foo, error) {

}

huangapple
  • 本文由 发表于 2014年1月13日 23:56:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/21095630.html
匿名

发表评论

匿名网友

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

确定