如何将数据库行转换为结构


78

假设我有一个结构:

type User struct {
    Name  string
    Id    int
    Score int
}

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

Answers:


95

Go软件包测试通常提供有关做事方式的线索。例如,来自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)

我知道这看起来与您的解决方案相似,但重要的是要显示如何找到解决方案。


75
如果简单的方法是手动将列绑定到结构字段,我想知道有什么困难的方法
Anthony Hunt

10
不幸的是,这不是很方便,特别是在较大的结构的情况下-手动绑定到结构属性完全失败...使用jmoiron / sqlx或其他一些库更有效...
shadyyx

我一直从rows.Scan()什么也没有得到。所有变量均设置为空。
filthy_wizard,2016年

14
这里的管道语法到底是什么?“选择|人”?我在godocs中看不到对此的任何引用。
布莱恩

9
与Brian有相同的WTF时刻。原来是他们纯粹用来测试sql / database(golang.org/src/database/sql/fakedb_test.go)制作的假驱动程序,我真的希望可以将其用于新代码!
Trey Stout,

57

我推荐github.com/jmoiron/sqlx

从自述文件:

sqlx是一个库,在go的标准database/sql库中提供了一组扩展 。的SQLX版本sql.DBsql.TXsql.Stmt,等。所有这些都使基础接口保持不变,因此它们的接口是标准接口的超集。这使得使用数据库/ sql和sqlx集成现有代码库变得相对容易。

其他主要概念是:

  • 将行编组为结构(具有嵌入式结构支持),地图和切片
  • 命名参数支持,包括准备好的语句
  • GetSelect快速从查询转到结构/切片

README还包括一个代码段,展示了将一行扫描到一个struct中:

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具有一些结构字段到数据库列的默认映射,并且能够使用标签指定数据库列(请注意上面结构的TelephoneCode字段Place)。您可以在文档中阅读有关此内容的更多信息。


38

这是一种实现方法-只需在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
    }
}

1
@eslammostafa该代码在什么时候会出现NULL值问题?
deFreitas

例如,@ deFreitas得分很高,我的意思是来自数据库的空值。
she7ata '16

@eslammostafa我知道,这真的很痛苦
deFreitas

@deFreitas在此期间,我使用sql.NullString和sql.NullInt64 ..etc来处理null值,不过这是额外的工作。
she7ata '16

@eslammostafa存在的问题是,如果您使用sql.NullString结构并将其转换为JSON然后生成的则不友好,需要使用aVO或类似的东西
deFreitas

8
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)
}

完整的例子


2

有一个专用的软件包:sqlstruct

不幸的是,我上次检查它不支持嵌入式结构(对于实现自己来说是微不足道的-我在几个小时内就有了一个工作原型)。

刚刚提交了我对sqlstruct所做的更改


0

使用: go-models-mysql sqlbuilder

val, err = m.ScanRowType(row, (*UserTb)(nil))

或完整的代码

import (
    "database/sql"
    "fmt"

    lib "github.com/eehsiao/go-models-lib"
    mysql "github.com/eehsiao/go-models-mysql"
)

// MyUserDao : extend from mysql.Dao
type MyUserDao struct {
    *mysql.Dao
}

// UserTb : sql table struct that to store into mysql
type UserTb struct {
    Name       sql.NullString `TbField:"Name"`
    Id         int            `TbField:"Id"`
    Score      int            `TbField:"Score"`
}

// GetFirstUser : this is a data logical function, you can write more logical in there
// sample data logical function to get the first user
func (m *MyUserDao) GetFirstUser() (user *User, err error) {

    m.Select("Name", "Id", "Score").From("user").Limit(1)
    fmt.Println("GetFirstUser", m.BuildSelectSQL().BuildedSQL())
    var (
        val interface{}
        row *sql.Row
    )

    if row, err = m.GetRow(); err == nil {
        if val, err = m.ScanRowType(row, (*UserTb)(nil)); err == nil {
            u, _ := val.(*UserTb)

            user = &User{
                Name:       lib.Iif(u.Name.Valid, u.Nae.String, "").(string),
                Id:         u.Id,
                Score:      u.Score,
            }
        }
    }
    row, val = nil, nil

    return
}

0

这是一个专门用于此的库:scany

您可以这样使用它:

type User struct {
    Name  string
    Id    int
    Score int
}

// db is your *sql.DB instance
// ctx is your current context.Context instance

// Use sqlscan.Select to query multiple records.
var users []*User
sqlscan.Select(ctx, db, &users, `SELECT name, id, score FROM users`)

// Use sqlscan.Get to query exactly one record.
var user User
sqlscan.Get(ctx, db, &user, `SELECT name, id, score FROM users WHERE id=123`)

它有据可查且易于使用。

免责声明:我是这个图书馆的作者。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.