英文:
Go language, scanning embeded struct with sqlx.StructScan
问题
我刚开始学习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
Author Author `db:"author"`
}
type Author 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 authors a on a.ID=b.author;")
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)
}
// 打印所有的书籍
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
结构体中的字段标签更改为:
Author Author `db:"author,prefix=auth."`
并将查询更改为:
rows, err := db.Queryx("Select b.*, auth.name from books b left outer join authors auth on auth.ID=b.author;")
但是没有任何变化。
编辑
经过几次尝试和错误,最后我让它正常工作了。
我稍微改变了我创建的模型。我将 Book
结构体从
type Book struct {
ID int
Title string
Year int
Author Author
}
改为
type Book struct {
ID int // 主键
Title string
Year int
AuthorID int `db:"author"` // 外键
Author
}
现在,它正常工作了。我之前的错误是,我将 Author
字段添加为 Author
类型。Sqlx 无法理解它。但是当我将 Author
添加为匿名嵌入的结构体时,问题解决了。
但是它引入了另一个问题
由于 Book
和 Author
结构体中都存在 ID
字段,ScanStruct 在所有行中将 Book.ID
填充为 0。
有没有什么办法可以避免这个问题?
英文:
I have just started learning Go language.
I wrote following simple program.
Here i am trying to fill struct with all Books and related Authers.
Book
struct has embeded Author
struct.
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)
}
}
But when i run it, it is giving following error
[samtech@sam sqlxapp]$ go run main.go
2016/02/11 18:45:46 Scan error: missing destination name name
exit status 1
What i am doing wrong?
I have also tried changing field tag in Book
struct to
Bauther Auther `db:"auther,prefix=auth."`
and change query to
rows, err := db.Queryx("Select b.*, auth.name from books b left outer join authers auth on auth.ID=b.auther;")
But it doesn't make any change.
EDIT
After few try and errors, finally i make it working.
I have to slightly change the Models that i have created. I changed Book struct from
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
}
Now, it is working fine. The mistake i was doing was, i added Bauther
field as Auther
. Sqlx could not understand it. But when i added Auther
as anonymous embedded struct, the problem resolved.
But it introduces another problem
As ID field exist in Book
as well as in Auther
both the structs. Now ScanStruct is filling Book.ID
with 0 in all rows.
Is there anything that i can do to avoid it?
答案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
语句中列的顺序相同的顺序进行扫描。
英文:
This doesn't seem possible with sqlx at the moment. There is a open issue for this: https://github.com/jmoiron/sqlx/issues/131
You can however do that easily without using 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)
}
}
Basically you have to be explicit of your columns in your SELECT
statement, so that you know the order of them. Then you create your struct and scan into each element with the same order as your columns in your SELECT
statement.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论