Gorm Golang orm 协会
Gorm Golang orm associations
我在 GORM ORM 中使用 Go。
我有以下结构。关系很简单。一个城镇有多个地方,一个地方属于一个城镇。
type Place struct {
ID int
Name string
Town Town
}
type Town struct {
ID int
Name string
}
现在我想查询所有地方,并与他们的所有字段一起查询相应城镇的信息。
这是我的代码:
db, _ := gorm.Open("sqlite3", "./data.db")
defer db.Close()
places := []Place{}
db.Find(&places)
fmt.Println(places)
我的示例数据库有这个数据:
/* places table */
id name town_id
1 Place1 1
2 Place2 1
/* towns Table */
id name
1 Town1
2 Town2
我收到这个:
[{1 Place1 {0 }} {2 Mares Place2 {0 }}]
但我期待收到这样的东西(两个地方都属于同一个城镇):
[{1 Place1 {1 Town1}} {2 Mares Place2 {1 Town1}}]
我怎样才能做这样的查询?我尝试使用 Preloads
和 Related
没有成功(可能是错误的方式)。我无法得到预期的结果。
您没有在 Place 结构中指定城镇的外键。只需将 TownId 添加到您的 Place 结构中,它就会起作用。
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)
}
}
TownID
必须指定为外键。 Place
结构是这样的:
type Place struct {
ID int
Name string
Description string
TownID int
Town Town
}
现在有不同的方法来处理这个问题。例如:
places := []Place{}
db.Find(&places)
for i, _ := range places {
db.Model(places[i]).Related(&places[i].Town)
}
这肯定会产生预期的结果,但请注意日志输出和触发的查询。
[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}]
输出是预期的,但这种方法有一个根本缺陷,请注意,对于每个地方都需要执行另一个数据库查询,这会产生 n + 1
问题。这可以解决问题,但随着地点数量的增加很快就会失控。
事实证明 good 方法使用预加载相当简单。
db.Preload("Town").Find(&places)
就是这样,产生的查询日志是:
[22.24ms] SELECT * FROM "places"
[0.92ms] SELECT * FROM "towns" WHERE ("id" in ('1'))
[{1 Place1 {1 Town1} 1} {2 Place2 {1 Town1} 1}]
这种方法只会触发两个查询,一个用于所有地点,一个用于所有有地点的城镇。这种方法可以很好地扩展地点和城镇的数量(在所有情况下只有两个查询)。
为了优化查询,我在相同情况下使用 "in condition"
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]
}
}
无需循环查找 ID,只需 pluck
ID
townIDs := []uint{}
DB.Model(&Place{}).Pluck("town_id", &placeIDs)
towns := []Town{}
DB.Where(townIDs).Find(&towns)
首先更改您的模型:
type Place struct {
ID int
Name string
Description string
TownID int
Town Town
}
其次,进行预加载:
https://gorm.io/docs/preload.html
总结:预加载一对一关系:有一个,属于
预加载:
db.Preload("Orders").Preload("Profile").Find(&users)
使用内部联接加入预加载:
db.Joins("Orders").Joins("Profile").Find(&users)
预加载所有关联:
db.Preload(clause.Associations).Find(&users)
我在 GORM ORM 中使用 Go。 我有以下结构。关系很简单。一个城镇有多个地方,一个地方属于一个城镇。
type Place struct {
ID int
Name string
Town Town
}
type Town struct {
ID int
Name string
}
现在我想查询所有地方,并与他们的所有字段一起查询相应城镇的信息。 这是我的代码:
db, _ := gorm.Open("sqlite3", "./data.db")
defer db.Close()
places := []Place{}
db.Find(&places)
fmt.Println(places)
我的示例数据库有这个数据:
/* places table */
id name town_id
1 Place1 1
2 Place2 1
/* towns Table */
id name
1 Town1
2 Town2
我收到这个:
[{1 Place1 {0 }} {2 Mares Place2 {0 }}]
但我期待收到这样的东西(两个地方都属于同一个城镇):
[{1 Place1 {1 Town1}} {2 Mares Place2 {1 Town1}}]
我怎样才能做这样的查询?我尝试使用 Preloads
和 Related
没有成功(可能是错误的方式)。我无法得到预期的结果。
您没有在 Place 结构中指定城镇的外键。只需将 TownId 添加到您的 Place 结构中,它就会起作用。
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)
}
}
TownID
必须指定为外键。 Place
结构是这样的:
type Place struct {
ID int
Name string
Description string
TownID int
Town Town
}
现在有不同的方法来处理这个问题。例如:
places := []Place{}
db.Find(&places)
for i, _ := range places {
db.Model(places[i]).Related(&places[i].Town)
}
这肯定会产生预期的结果,但请注意日志输出和触发的查询。
[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}]
输出是预期的,但这种方法有一个根本缺陷,请注意,对于每个地方都需要执行另一个数据库查询,这会产生 n + 1
问题。这可以解决问题,但随着地点数量的增加很快就会失控。
事实证明 good 方法使用预加载相当简单。
db.Preload("Town").Find(&places)
就是这样,产生的查询日志是:
[22.24ms] SELECT * FROM "places"
[0.92ms] SELECT * FROM "towns" WHERE ("id" in ('1'))
[{1 Place1 {1 Town1} 1} {2 Place2 {1 Town1} 1}]
这种方法只会触发两个查询,一个用于所有地点,一个用于所有有地点的城镇。这种方法可以很好地扩展地点和城镇的数量(在所有情况下只有两个查询)。
为了优化查询,我在相同情况下使用 "in condition"
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]
}
}
无需循环查找 ID,只需 pluck
ID
townIDs := []uint{}
DB.Model(&Place{}).Pluck("town_id", &placeIDs)
towns := []Town{}
DB.Where(townIDs).Find(&towns)
首先更改您的模型:
type Place struct {
ID int
Name string
Description string
TownID int
Town Town
}
其次,进行预加载: https://gorm.io/docs/preload.html
总结:预加载一对一关系:有一个,属于
预加载:
db.Preload("Orders").Preload("Profile").Find(&users)
使用内部联接加入预加载:
db.Joins("Orders").Joins("Profile").Find(&users)
预加载所有关联:
db.Preload(clause.Associations).Find(&users)