2013-06-23 47 views
30

比方说,我有一个结构:如何在Go中将数据库行转换为结构体?

type User struct { 
    Name string 
    Id int 
    Score int 
} 

而且具有相同的架构的数据库表。将数据库行解析为结构最简单的方法是什么?我在下面添加了一个答案,但我不确定它是最好的答案。

回答

17

下面介绍一种方法 - 只需在Scan函数中手动分配所有结构值即可。

func getUser(name string) (*User, error) { 
    var u User 
    // this calls sql.Open, etc. 
    db := getConnection() 
    // note the below syntax only works for postgres 
    err := db.QueryRow("SELECT * FROM users WHERE name = $1", name).Scan(&u.Id, &u.Name, &u.Score) 
    if err != nil { 
     return &User{}, err 
    } else { 
     return &u, nil 
    } 
} 
+2

怎么会空值的工作? – emostafa

+0

@eslammostafa在什么时候这个代码可以有NULL值的问题? – deFreitas

+0

@deFreitas在比分例如,我的意思是来自数据库的空值。 – emostafa

40

去包测试通常提供线索的方式做事。例如,从database/sql/sql_test.go

func TestQuery(t *testing.T) { 
    /* . . . */ 
    rows, err := db.Query("SELECT|people|age,name|") 
    if err != nil { 
      t.Fatalf("Query: %v", err) 
    } 
    type row struct { 
      age int 
      name string 
    } 
    got := []row{} 
    for rows.Next() { 
      var r row 
      err = rows.Scan(&r.age, &r.name) 
      if err != nil { 
        t.Fatalf("Scan: %v", err) 
      } 
      got = append(got, r) 
    } 
    /* . . . */ 
} 

func TestQueryRow(t *testing.T) { 
    /* . . . */ 
    var name string 
    var age int 
    var birthday time.Time 
    err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age) 
    /* . . . */ 
} 

其中,对于你的问题,查询行插入结构,将转化为类似:

var row struct { 
    age int 
    name string 
} 
err = db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&row.age, &row.name) 

我知道,类似于您的解决方案,但它的重要的是展示如何找到解决方案。

+10

如果简单的方法是手动将列绑定到struct字段我想知道什么是困难的方法 –

+1

不幸的是,这并不是很方便,尤其是在更大的结构的情况下 - 手动绑定结构属性是完全失败的...使用jmoiron/sqlx或其他库更有效率... – shadyyx

+0

我一直没有收回rows.Scan ()。所有的变量都被设置为空。 –

2

有包只是为:sqlstruct

不幸的是,我检查它不支持嵌入式结构上一次(这是容易实现自己 - 我曾在几个小时一个工作原型)。

只是承诺我对sqlstruct

0

您可以映射行制作成使用github.com/gocraft/dbr(godoc)结构的变化。

import (
    "github.com/gocraft/dbr" 
) 

func GetUser(name string) (*User, error) { 
    var u User 
    rows, err := db.Query("SELECT * FROM users WHERE name = $1 LIMIT 1", name) 
    if err != nil { 
     return nil, err 
    } 
    // Load uses reflection to map values into a struct. 
    n, err := dbr.Load(rows, &u) 
    if err != nil { 
     return nil, err 
    } 
    if n != 1 { 
     return nil, NotFound 
    } 
    return u, nil 

} 
11

我推荐github.com/jmoiron/sqlx

自述:

SQLX是提供随时随地的标准 database/sql库一组扩展库。 sqlx版本的sql.DB,sql.TX, sql.Stmt,et al。所有的接口都保持不变,所以它们的接口是标准接口的超集。这使得使用 数据库/ sql与sqlx集成现有代码库相对简单。

主要补充的概念是:

  • 元帅行到结构(具有嵌入式结构的支持),地图,和切片
  • 命名参数支持,包括准备好的发言
  • GetSelect从查询赶紧去结构/切片

自述还包括代码片段演示扫描一行到一个结构:

type Place struct { 
    Country  string 
    City   sql.NullString 
    TelephoneCode int `db:"telcode"` 
} 
// Loop through rows using only one struct 
place := Place{} 
rows, err := db.Queryx("SELECT * FROM place") 
for rows.Next() { 
    err := rows.StructScan(&place) 
    if err != nil { 
     log.Fatalln(err) 
    } 
    fmt.Printf("%#v\n", place) 
} 

需要注意的是,我们没有每列手动映射到结构的领域。 sqlx具有结构字段到数据库列的一些默认映射,以及能够使用标签指定数据库列(注意上面的Place结构的TelephoneCode字段)。你可以在the documentation阅读更多。

+1

谢谢你的提示! –

1
rows, err := connection.Query("SELECT `id`, `username`, `email` FROM `users`") 

if err != nil { 
    panic(err.Error()) 
} 

for rows.Next() { 
    var user User 

    if err := rows.Scan(&user.Id, &user.Username, &user.Email); err != nil { 
     log.Println(err.Error()) 
    } 

    users = append(users, user) 
} 

Full example

相关问题