open orm go go-gorm

open - Gorm Golang orm Asociaciones



join gorm (3)

No especifica la clave externa de las ciudades en la estructura de su Lugar. Simplemente agregue TownId a su estructura Place y debería funcionar.

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

Estoy usando Go con el GORM ORM . Tengo las siguientes estructuras. La relación es simple. Una ciudad tiene varios lugares y un lugar pertenece a una ciudad.

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

Ahora quiero consultar todos los lugares y llevarme bien con todos sus campos la información de la ciudad correspondiente. Este es mi código:

db, _ := gorm.Open("sqlite3", "./data.db") defer db.Close() places := []Place{} db.Find(&places) fmt.Println(places)

Mi base de datos de muestra tiene estos datos:

/* places table */ id name town_id 1 Place1 1 2 Place2 1 /* towns Table */ id name 1 Town1 2 Town2

estoy recibiendo esto:

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

Pero estoy esperando recibir algo como esto (ambos lugares pertenecen a la misma ciudad):

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

¿Cómo puedo hacer dicha consulta? Intenté usar Preloads y Related sin éxito (probablemente de forma incorrecta). No puedo ponerme a trabajar el resultado esperado.


Para optimizar la consulta utilizo "in condition" en la misma situación

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


TownID debe especificarse como la clave externa. La estructura del Place pone así:

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

Ahora hay diferentes enfoques para manejar esto. Por ejemplo:

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

Esto ciertamente producirá el resultado esperado, pero observe la salida del registro y las consultas activadas.

[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}]

El resultado es el esperado, pero este enfoque tiene un defecto fundamental. Tenga en cuenta que para cada lugar existe la necesidad de realizar otra consulta de db que genere un problema de n + 1 . Esto podría resolver el problema pero se saldrá de control rápidamente a medida que la cantidad de lugares crezca.

Resulta que el buen enfoque es bastante simple usando precargas.

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

Eso es todo, el registro de consultas producido es:

[22.24ms] SELECT * FROM "places" [0.92ms] SELECT * FROM "towns" WHERE ("id" in (''1'')) [{1 Place1 {1 Town1} 1} {2 Place2 {1 Town1} 1}]

Este enfoque solo activará dos consultas, una para todos los lugares y otra para todas las ciudades que tienen lugares. Este enfoque se adapta bien a la cantidad de lugares y ciudades (solo dos consultas en todos los casos).