当前位置: 首页 > 面试题库 >

Gorm Golang Orm协会

权胜泫
2023-03-14
问题内容

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


问题答案:

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

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



 类似资料:
  • Tornado 中推荐用 协程 来编写异步代码. 协程使用 Python 中的关键字 yield 来替代链式回调来实现挂起和继续程序的执行(像在 gevent 中使用的轻量级线程合作的方法有时也称作协程, 但是在 Tornado 中所有协程使用异步函数来实现的明确的上下文切换). 协程和异步编程的代码一样简单, 而且不用浪费额外的线程, . 它们还可以减少上下文切换 让并发更简单 . Exampl

  • Git 可以使用四种主要的协议来传输资料:本地协议(Local),HTTP 协议,SSH(Secure Shell)协议及 Git 协议。 在此,我们将会讨论那些协议及哪些情形应该使用(或避免使用)他们。 本地协议 最基本的就是 本地协议(Local protocol) ,其中的远程版本库就是硬盘内的另一个目录。 这常见于团队每一个成员都对一个共享的文件系统(例如一个挂载的 NFS)拥有访问权,或

  • 协议为方法、属性、以及其他特定的任务需求或功能定义蓝图。协议可被类、结构体、或枚举类型采纳以提供所需功能的具体实现。满足了协议中需求的任意类型都叫做遵循了该协议。 除了指定遵循类型必须实现的要求外,你可以扩展一个协议以实现其中的一些需求或实现一个符合类型的可以利用的附加功能。 协议的语法 定义协议的方式与类、结构体、枚举类型非常相似: protocol SomeProtocol { //

  • defer 协程客户端的对象结构体,设置client->defer = 1表示启用了defer延迟收包 设置client->defer_yield = 1表示进入了wait状态 事件监听 因为swoole底层的EventLoop总是在运行的,因此可能某个协程客户端没有yield也会收到包。底层需要对数据进行缓存。 Client 自动保存到ccp->result内存中。为了避免收到的数据过多,导致内

  • 本页包含内容: 协议的语法(Protocol Syntax) 对属性的规定(Property Requirements) 对方法的规定(Method Requirements) 对突变方法的规定(Mutating Method Requirements) 对构造器的规定(Initializer Requirements) 协议类型(Protocols as Types) 委托(代理)模式(Dele

  • 使用 Git 大家可以一起协作开发一个项目。现在介绍几种协作开发用的工作流程,假设项目的发起者叫王皓(wanghao8080),一起协同开发的有个人叫小雪(xiaoxue8080)。 集中式工作流 功能分支工作流 Gitflow 工作流 Forking 工作流

  • 概念 Hyperf 是运行于 Swoole 4 的协程之上的,这也是 Hyperf 能提供高性能的其中一个很大的因素。 PHP-FPM 的运作模式 在聊协程是什么之前,我们先聊聊传统 PHP-FPM 架构的运作模式,PHP-FPM 是一个多进程的 FastCGI 管理程序,是绝大多数 PHP 应用所使用的运行模式。假设我们使用 Nginx 提供 HTTP 服务(Apache 同理),所有客户端发起

  • Python的协程很像生成器,但并不是生成数据,大多数时候扮演了数据消费者的作用。换句话说,协程是一个在每次使用send方法发送数据后就会被唤醒的函数。 协程的要点是将“yield”关键字写在表达式的右边。下面是一个打印出所发送的值的协程例子: def coroutine(): print('My coroutine') while True: val = yiel