Gorm Golang orm associations

J'utilise Go avec le Gorm ORM . J'ai le texte suivant des structures. La relation est simple. Une ville a plusieurs endroits et un endroit appartient à une ville.

type Place struct {
  ID          int
  Name        string
  Town        Town
}

type Town struct {
  ID   int
  Name string
}

Maintenant, je veux interroger tous les lieux et m'entendre avec tous leurs champs les informations de la ville correspondante. C'est mon code:

db, _ := gorm.Open("sqlite3", "./data.db")
defer db.Close()

places := []Place{}
db.Find(&places)
fmt.Println(places)

Mon exemple de base de données contient les données suivantes:

/* places table */
id  name    town_id
 1  Place1        1
 2  Place2        1

/* towns Table */
id name
 1 Town1
 2 Town2

Je reçois ceci:

[{1 Place1 {0 }} {2 Mares Place2 {0 }}]

Mais je m'attends à ce que reçoive quelque chose comme ceci (les deux endroits appartiennent à la même ville):

[{1 Place1 {1 Town1}} {2 Mares Place2 {1 Town1}}]

Comment puis-je faire une telle requête ? J'ai essayé d'utiliser Preloads et Related sans succès (probablement dans le mauvais sens). Je ne peux pas obtenir le résultat attendu.

22
demandé sur Javier Cadiz 2015-04-03 19:13:21

3 réponses

TownID doit être spécifié comme clé étrangère. La structure Place obtient comme ceci:

type Place struct {
  ID          int
  Name        string
  Description string
  TownID      int
  Town        Town
}

Maintenant, il existe une approche différente pour gérer cela. Par exemple:

places := []Place{}
db.Find(&places)
for i, _ := range places {
    db.Model(places[i]).Related(&places[i].Town)
}

Cela produira certainement le résultat attendu, mais notez la sortie du journal et les requêtes déclenchées.

[4.76ms]  SELECT  * FROM "places"
[1.00ms]  SELECT  * FROM "towns"  WHERE ("id" = '1')
[0.73ms]  SELECT  * FROM "towns"  WHERE ("id" = '1')

[{1 Place1  {1 Town1} 1} {2 Place2  {1 Town1} 1}]

La sortie est attendue mais cette approche a un défaut fondamental, notez que pour chaque endroit, il est nécessaire de faire une autre requête db qui produit un problème n + 1. Cela pourrait résoudre le problème, mais va rapidement devient hors de contrôle que la quantité de places se développent.

Il s'avère que l'approche good est assez simple en utilisant des préchargements.

db.Preload("Town").Find(&places)

Voilà, le journal de requête produit est:

[22.24ms]  SELECT  * FROM "places"
[0.92ms]  SELECT  * FROM "towns"  WHERE ("id" in ('1'))

[{1 Place1  {1 Town1} 1} {2 Place2  {1 Town1} 1}]

Cette approche ne déclenchera que deux requêtes, une pour tous les lieux, et une pour toutes les villes qui ont des lieux. Cette approche évolue bien en ce qui concerne le nombre de lieux et de villes (seulement deux requêtes dans tous les cas).

35
répondu Javier Cadiz 2015-04-11 16:07:16

Vous ne spécifiez pas la clé étrangère des villes dans votre structure de Place. Ajoutez simplement TownId à votre structure de Place et cela devrait fonctionner.

package main

import (
    "fmt"

    "github.com/jinzhu/gorm"
    _ "github.com/mattn/go-sqlite3"
)

type Place struct {
    Id     int
    Name   string
    Town   Town
    TownId int //Foregin key
}

type Town struct {
    Id   int
    Name string
}

func main() {
    db, _ := gorm.Open("sqlite3", "./data.db")
    defer db.Close()

    db.CreateTable(&Place{})
    db.CreateTable(&Town{})
    t := Town{
        Name: "TestTown",
    }

    p1 := Place{
        Name:   "Test",
        TownId: 1,
    }

    p2 := Place{
        Name:   "Test2",
        TownId: 1,
    }

    err := db.Save(&t).Error
    err = db.Save(&p1).Error
    err = db.Save(&p2).Error
    if err != nil {
        panic(err)
    }

    places := []Place{}
    err = db.Find(&places).Error
    for i, _ := range places {
        db.Model(places[i]).Related(&places[i].Town)
    }
    if err != nil {
        panic(err)
    } else {
        fmt.Println(places)
    }
}
4
répondu olif 2015-04-06 20:20:36

Pour optimiser la requête, j'utilise "in condition" dans la même situation

places := []Place{}

DB.Find(&places)

keys := []uint{}
for _, value := range places {
    keys = append(keys, value.TownID)
}

rows := []Town{}
DB.Where(keys).Find(&rows)

related := map[uint]Town{}
for _, value := range rows {
    related[value.ID] = value
}

for key, value := range places {
    if _, ok := related[value.TownID]; ok {
        res[key].Town = related[value.TownID]
    }
}
4
répondu Max 2016-04-17 18:48:21