我在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没有成功(可能是错误的方式)。我无法获得预期的结果。
Preloads
Related
TownID必须指定为外键。该Place结构如下所示:
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查询。这可以解决问题,但随着场所数量的增加,将很快失去控制。
n + 1
事实证明,使用预加载是一种 很好的 方法。
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}]
这种方法只会触发两个查询,一个查询所有地点,一个查询所有有地点的城镇。这种方法可以很好地扩展地方和城镇的数量(在所有情况下都只有两个查询)。