小编典典

Gorm Golang Orm协会

go

我在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没有成功(可能是错误的方式)。我无法获得预期的结果。


阅读 247

收藏
2020-07-02

共1个答案

小编典典

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问题的db查询。这可以解决问题,但随着场所数量的增加,将很快失去控制。

事实证明,使用预加载是一种 很好的 方法。

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

这种方法只会触发两个查询,一个查询所有地点,一个查询所有有地点的城镇。这种方法可以很好地扩展地方和城镇的数量(在所有情况下都只有两个查询)。

2020-07-02