Go语言,使用sqlx.StructScan扫描嵌入式结构体

3

我刚开始学习Go语言。

我写了下面这个简单的程序。

在这里,我试图用所有书籍和相关作者填充结构体。

Book 结构体嵌入了 Author 结构体。

package main
import (
    "fmt"
    "log"
    "time"
    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq"
)

type Book struct {
    ID      int
    Title   string
    Year    int
    Bauther  Auther `db:"auther"`
}

type Auther struct {
    ID      int
    Name    string
    Dob     time.Time
}

func main() {
    db, err := sqlx.Open("postgres", "host=localhost user=testuser dbname=testdb password=testuser")
    if err != nil {
       log.Fatal("DB Conn error: ", err)
    }

    if err = db.Ping(); err != nil {
        log.Fatal("DB Ping error: ", err)
    }
    defer db.Close()

    rows, err := db.Queryx("Select b.*, a.name from books b left outer join authers a on a.ID=b.auther;")
    if err != nil {
        log.Fatal("DB Query error: ", err)
    }
    defer rows.Close()

    var books []*Book
    for rows.Next() {
        var b = &Book{}
        err := rows.StructScan(b)
        if err != nil {
            log.Fatal("Scan error: ", err)
        }
        books = append(books, b)
    }

    // print all books
    for _, b := range books {
        fmt.Printf("%v", b)
    }
}

但是当我运行它时,会出现以下错误。
[samtech@sam sqlxapp]$ go run main.go
2016/02/11 18:45:46 Scan error: missing destination name name
exit status 1

我做错了什么?

我还尝试将Book结构体中的字段标签更改为

Bauther  Auther `db:"auther,prefix=auth."`

并将查询更改为

rows, err := db.Queryx("Select b.*, auth.name from books b left outer join authers auth on auth.ID=b.auther;")

但这并没有产生任何变化。

编辑

经过多次尝试和错误,最终我让它工作了。

我不得不略微更改我创建的模型。我将Book结构体从

type Book struct {
    ID      int
    Title   string
    Year    int
    Bauther Auther
}

to

type Book struct {
    ID          int                  // Key
    Title       string
    Year        int
    AutherID    int `db:"auther"`   // FKey
    Auther 
}

现在,它工作得很好。我的错误在于将 Bauther 字段添加为 Auther。Sqlx 不能理解它。但是当我将 Auther 添加为匿名嵌入式结构时,问题得到了解决。
但是它引入了另一个问题 :)
由于 ID 字段存在于 Book 和 Auther 中的两个结构体中,因此 ScanStruct 在所有行中都使用 0 填充 Book.ID。
有什么方法可以避免这种情况吗?
1个回答

5
目前使用sqlx似乎不可能实现这一点。有一个相关的未解决问题:https://github.com/jmoiron/sqlx/issues/131 但是,您可以轻松地在不使用sqlx的情况下实现它:
package main

import (
    "database/sql"
    "fmt"
    "log"
    "time"

    _ "github.com/lib/pq"
)

type Book struct {
    ID      int
    Title   string
    Year    int
    Bauther Auther
}

type Auther struct {
    ID   int
    Name string
    Dob  time.Time
}

func main() {
    db, err := sql.Open("postgres", "postgres://localhost/testdb?sslmode=disable")
    if err != nil {
        log.Fatal("DB Conn error: ", err)
    }

    if err = db.Ping(); err != nil {
        log.Fatal("DB Ping error: ", err)
    }
    defer db.Close()

    rows, err := db.Query("Select b.id, b.title, b.year, a.id, a.name, a.dob from books b left outer join authers a on a.ID=b.auther;")
    if err != nil {
        log.Fatal("DB Query error: ", err)
    }
    defer rows.Close()

    var books []*Book
    for rows.Next() {
        var b = &Book{}
        if err := rows.Scan(&b.ID, &b.Title, &b.Year, &b.Bauther.ID, &b.Bauther.Name, &b.Bauther.Dob); err != nil {
            log.Fatal(err)
        }
        books = append(books, b)
    }

    // print all books
    for _, b := range books {
        fmt.Printf("%v", b)
    }
}

基本上,您必须在SELECT语句中明确指定列,以便您知道它们的顺序。然后,您创建结构并按与SELECT语句中列相同的顺序扫描每个元素。


是的,我可以使用Scan方法,但在一个大型项目中,查询仅有少数列不同的情况下,手动传递字段给Scan将会很麻烦。 - SamTech

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接