web-dev-qa-db-fra.com

Comment convertir une ligne de base de données en structure, dans Go?

Disons que j'ai un struct:

type User struct {
    Name  string
    Id    int
    Score int
}

Et une table de base de données avec le même schéma. Quel est le moyen le plus simple d'analyser une ligne de base de données dans une structure? J'ai ajouté une réponse ci-dessous mais je ne suis pas sûr que ce soit la meilleure.

56
Kevin Burke

Voici une façon de le faire - assignez simplement toutes les valeurs de structure manuellement dans la fonction 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

Les tests de paquets donnent souvent des indices sur la façon de faire les choses. Par exemple, de 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)
    /* . . . */
}

Ce qui, pour votre question, interroger une ligne dans une structure, se traduirait par quelque chose comme:

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

Je sais que cela ressemble à votre solution, mais il est important de montrer comment trouver une solution.

77
peterSO

Je recommande github.com/jmoiron/sqlx .

Du README:

sqlx est une librairie qui fournit un ensemble d'extensions sur le standard de go database/sql bibliothèque. Les versions sqlx de sql.DB, sql.TX, sql.Stmt, et al. tous laissent les interfaces sous-jacentes intactes, de sorte que leurs interfaces sont un sur-ensemble sur les standards. Cela rend relativement facile l'intégration de bases de code existantes en utilisant database/sql avec sqlx.

Les principaux concepts supplémentaires sont:

  • Décalez les lignes dans des structures (avec prise en charge des structures intégrées), des cartes et des tranches
  • Prise en charge des paramètres nommés, y compris les instructions préparées
  • Get et Select pour passer rapidement d'une requête à une structure/une tranche

Le README comprend également un extrait de code montrant le scan d’une ligne dans une structure:

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

Notez que nous n'avons pas eu à mapper manuellement chaque colonne sur un champ de la structure. sqlx a certains mappages par défaut pour les champs de struct aux colonnes de base de données, ainsi que pour pouvoir spécifier des colonnes de base de données à l'aide de balises (notez le champ TelephoneCode de la structure Place ci-dessus). Vous pouvez en savoir plus à ce sujet dans la documentation .

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

Exemple complet

il y a juste un paquet pour ça: sqlstruct

malheureusement, la dernière fois que j'ai vérifié, cela ne supportait pas les structures intégrées (qui sont simples à implémenter vous-même - j'ai eu un prototype fonctionnel en quelques heures).

viens de valider les modifications que j'ai apportées à sqlstruct

2
deemok

Vous pouvez mapper les lignes dans des structures en utilisant 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