英文:
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.DB
、sql.TX
、sql.Stmt
等都保持了底层接口不变,因此它们的接口是标准接口的超集。这使得将使用database/sql的现有代码库与sqlx集成相对容易。
主要的附加概念有:
- 将行解析为结构体(支持嵌套结构体)、映射和切片
- 支持命名参数,包括预处理语句
Get
和Select
可以快速从查询结果转换为结构体/切片
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:"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)
}
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("SELECT * FROM users WHERE name = $1", name).Scan(&u.Id, &u.Name, &u.Score)
if err != nil {
return &User{}, err
} else {
return &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("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)
}
答案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, &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`)
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 (
"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"`
}
// Just a simple struct containing the table name and
// the name of the ID column or columns:
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")
}
// Lets insert a user so the query below has something to return:
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}
}
答案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 (
"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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论