web-dev-qa-db-ja.com

Goでデータベース行を構造体に変換するにはどうすればよいですか?

構造体があるとしましょう:

type User struct {
    Name  string
    Id    int
    Score int
}

そして、同じスキーマを持つデータベーステーブル。データベース行を構造体に解析する最も簡単な方法は何ですか?以下に回答を追加しましたが、それが最良のものかどうかはわかりません。

56
Kevin Burke

これを行う1つの方法があります-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
    }
}
31
Kevin Burke

Goパッケージテストは、多くの場合、物事の実行方法に関する手がかりを提供します。たとえば、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)
    /* . . . */
}

あなたの質問のために、構造に行を照会すると、次のようなものに変換されます:

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

それはあなたのソリューションに似ていますが、ソリューションを見つける方法を示すことが重要です。

77
peterSO

github.com/jmoiron/sqlx をお勧めします。

READMEから:

sqlxは、goの標準database/sqlライブラリで一連の拡張機能を提供するライブラリです。 sql.DBsql.TXsql.Stmtなどのsqlxバージョン。基礎となるインターフェースはすべてそのままであるため、それらのインターフェースは標準インターフェースのスーパーセットです。これにより、データベース/ SQLとsqlxを使用して既存のコードベースを統合するのが比較的簡単になります。

主な追加概念は次のとおりです。

  • 行を構造体(埋め込み構造体サポート付き)、マップ、およびスライスにマーシャルします。
  • 準備済みステートメントを含む名前付きパラメーターのサポート
  • GetおよびSelectは、クエリから構造/スライスにすばやく移動します

READMEには、構造体への行のスキャンを示すコードスニペットも含まれています。

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フィールドに注意してください)。詳細については ドキュメント をご覧ください。

36
ckeeney
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)
}

完全な例

そのためのパッケージがあります: sqlstruct

残念ながら、前回チェックしたとき、埋め込み構造体をサポートしていませんでした(自分で実装するのは簡単です-数時間でプロトタイプが動作しました)。

sqlstruct に加えた変更をコミットしました

2
deemok

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

}
1
AJcodez