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

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

How do I convert a database row into a struct

问题

让我们假设我有一个结构体:

type User struct {
    Name  string
    Id    int
    Score int
}

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

英文:

Let's say I have a struct:

type User struct {
    Name  string
    Id    int
    Score int
}

And a database table with the same schema. What's the easiest way to parse a database row into a struct? I've added an answer below but I'm not sure it's the best one.

答案1

得分: 116

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)

英文:

Go package tests often provide clues as to ways of doing things. For example, from 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)
    /* . . . */
}

Which, for your question, querying a row into a structure, would translate to something like:

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

I know that looks similar to your solution, but it's important to show how to find a solution.
1: https://golang.org/src/database/sql/sql_test.go?h=TestQuery#L252

答案2

得分: 74

我推荐使用github.com/jmoiron/sqlx

从README中可以看到:

sqlx是一个在go的标准database/sql库上提供一组扩展的库。sqlx版本的sql.DBsql.TXsql.Stmt等都保持了底层接口不变,因此它们的接口是标准接口的超集。这使得将使用database/sql的现有代码库与sqlx集成相对容易。

主要的附加概念有:

  • 将行解析为结构体(支持嵌套结构体)、映射和切片
  • 支持命名参数,包括预处理语句
  • GetSelect可以快速从查询结果转换为结构体/切片

README还包含了一个代码片段,演示了将行扫描到结构体中:

type Place struct {
    Country       string
    City          sql.NullString
    TelephoneCode int `db:"telcode"`
}
// 使用一个结构体循环遍历行
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字段)。您可以在文档中了解更多信息。

英文:

I recommend github.com/jmoiron/sqlx.

From the README:

> sqlx is a library which provides a set of extensions on go's standard
> database/sql library. The sqlx versions of sql.DB, sql.TX,
> sql.Stmt, et al. all leave the underlying interfaces untouched, so
> that their interfaces are a superset on the standard ones. This makes
> it relatively painless to integrate existing codebases using
> database/sql with sqlx.
>
> Major additional concepts are:
>
> * Marshal rows into structs (with embedded struct support), maps, and slices
> * Named parameter support including prepared statements
> * Get and Select to go quickly from query to struct/slice


The README also includes a code snippet demonstrating scanning a row into a struct:

<!-- language: lang-golang -->

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

Note that we didn't have to manually map each column to a field of the struct. sqlx has some default mappings for struct fields to database columns, as well as being able to specify database columns using tags (note the TelephoneCode field of the Place struct above). You can read more about that in the documentation.

答案3

得分: 45

这是一种方法 - 只需在Scan函数中手动分配所有结构值。

func getUser(name string) (*User, error) {
	var u User
    // 这里调用了sql.Open等函数
	db := getConnection()
    // 注意下面的语法只适用于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
	}
}
英文:

Here's one way to do it - just assign all of the struct values manually in the Scan function.

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(&quot;SELECT * FROM users WHERE name = $1&quot;, name).Scan(&amp;u.Id, &amp;u.Name, &amp;u.Score)
	if err != nil {
		return &amp;User{}, err
	} else {
		return &amp;u, nil
	}
}

答案4

得分: 7

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)

}

英文:
rows, err := connection.Query(&quot;SELECT `id`, `username`, `email` FROM `users`&quot;)

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

for rows.Next() {
	var user User

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

	users = append(users, user)
}

Full example

答案5

得分: 6

这里有一个专门用于此目的的库:scany

您可以像这样使用它:

type User struct {
    Name  string
    Id    int
    Score int
}

// db 是您的 *sql.DB 实例
// ctx 是您当前的 context.Context 实例

// 使用 sqlscan.Select 查询多条记录。
var users []*User
sqlscan.Select(ctx, db, &users, `SELECT name, id, score FROM users`)

// 使用 sqlscan.Get 查询一条记录。
var user User
sqlscan.Get(ctx, db, &user, `SELECT name, id, score FROM users WHERE id=123`)

它有很好的文档并且易于使用。

免责声明:我是这个库的作者。

英文:

Here is a library just for that: scany.

You can use it like that:

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, &amp;users, `SELECT name, id, score FROM users`)

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

It's well documented and easy to work with.

Disclaimer: I am the author of this library.

答案6

得分: 1

有一个专门用于这个的包:sqlstruct

不幸的是,上次我检查时它不支持嵌入结构体(你可以自己实现,我几个小时内就有一个可工作的原型)。

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

英文:

there's package just for that: sqlstruct

unfortunately, last time I checked it did not support embedded structs (which are trivial to implement yourself - i had a working prototype in a few hours).

just committed the changes I made to sqlstruct

答案7

得分: 0

这里已经有一些很好的答案了,我想要添加一个我自己写的SQL库,专门用来解决在最常用的Golang库上执行简单查询时的复杂性问题;它叫做KSQL(不是Kafka的那个,K代表Keep It Stupid Simple,即保持简单)。

使用它,你可以将用户扫描到一个类似这样的结构体中:

import (
	"context"
	"fmt"

	"github.com/vingarcia/ksql"
	"github.com/vingarcia/ksql/adapters/kpgx"
)

type User struct {
	Id    int    `ksql:"id"`
	Name  string `ksql:"name"`
	Score int    `ksql:"score"`
}

// 只是一个简单的结构体,包含表名和ID列的名称:
var UsersTable = ksql.NewTable("users", "id")

func main() {
	ctx := context.Background()
	dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable TimeZone=UTC"
	db, err := kpgx.New(ctx, dsn, ksql.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// 让我们插入一个用户,这样下面的查询就有返回值了:
	err = db.Insert(ctx, UsersTable, &User{
		Name:  "SomeUser",
		Score: 42,
	})
	if err != nil {
		panic(err)
	}

	var user User
	err = db.QueryOne(ctx, &user, "FROM users WHERE name = $1", "SomeUser")
	if err != nil {
		panic(err)
	}

	fmt.Printf("%+v\n", user) // {Id:1 Name:SomeUser Score:42}
}
英文:

There are already some good answers here, I would like to add an SQL library I wrote just for tackling this issue of how complex it is to do a simple query on the most used Golang libraries; its called KSQL (not the Kafka one, the K stands for KISS or Keep It Stupid Simple).

Using it you could scan a user into a struct like this:

import (
	&quot;context&quot;
	&quot;fmt&quot;

	&quot;github.com/vingarcia/ksql&quot;
	&quot;github.com/vingarcia/ksql/adapters/kpgx&quot;
)

type User struct {
	Id    int    `ksql:&quot;id&quot;`
	Name  string `ksql:&quot;name&quot;`
	Score int    `ksql:&quot;score&quot;`
}

// Just a simple struct containing the table name and
// the name of the ID column or columns:
var UsersTable = ksql.NewTable(&quot;users&quot;, &quot;id&quot;)

func main() {
	ctx := context.Background()
	dsn := &quot;host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable TimeZone=UTC&quot;
	db, err := kpgx.New(ctx, dsn, ksql.Config{})
	if err != nil {
		panic(&quot;failed to connect database&quot;)
	}

	// Lets insert a user so the query below has something to return:
	err = db.Insert(ctx, UsersTable, &amp;User{
		Name:  &quot;SomeUser&quot;,
		Score: 42,
	})
	if err != nil {
		panic(err)
	}

	var user User
	err = db.QueryOne(ctx, &amp;user, &quot;FROM users WHERE name = $1&quot;, &quot;SomeUser&quot;)
	if err != nil {
		panic(err)
	}

	fmt.Printf(&quot;%+v\n&quot;, user) // {Id:1 Name:SomeUser Score:42}
}

答案8

得分: -1

使用:
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
}
英文:

use :
go-models-mysql
sqlbuilder

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

or the full code

import (
	&quot;database/sql&quot;
	&quot;fmt&quot;

	lib &quot;github.com/eehsiao/go-models-lib&quot;
	mysql &quot;github.com/eehsiao/go-models-mysql&quot;
)

// 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:&quot;Name&quot;`
	Id         int            `TbField:&quot;Id&quot;`
	Score      int            `TbField:&quot;Score&quot;`
}

// 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(&quot;Name&quot;, &quot;Id&quot;, &quot;Score&quot;).From(&quot;user&quot;).Limit(1)
	fmt.Println(&quot;GetFirstUser&quot;, 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 = &amp;User{
				Name:       lib.Iif(u.Name.Valid, u.Nae.String, &quot;&quot;).(string),
				Id:         u.Id,
				Score:      u.Score,
			}
		}
	}
	row, val = nil, nil

	return
}

huangapple
  • 本文由 发表于 2013年6月24日 05:22:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/17265463.html
匿名

发表评论

匿名网友

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

确定