【问题标题】:What is the difference between explicit and implicit JOIN in JPA? (performance)JPA 中的显式和隐式 JOIN 有什么区别? (表现)
【发布时间】:2018-09-05 14:28:06
【问题描述】:

这些天我正在阅读有关JPA 的信息。我了解到可以在 JPQL 中使用 explicitimplicit JOIN

显式连接

em.createQuery(“SELECT b.title, p.name FROM Book b JOIN b.publisher p”).getResultList();

隐式连接

em.createQuery(“SELECT b.title, b.publisher.name FROM Book b”).getResultList();

这些例子的来源:link

我的问题是:explicitimplict JOIN 在性能方面有什么区别吗?


更新

我已经阅读了@MatteoBaldi 和@Kayaman 所写的内容,我已经做了一些测试,我想与你分享结果。

我创建了两个实体:studentscourse,我有一个 manyToOne(许多学生参加一门课程)。我用EcpliseLink实现JPA

Query = 从课程中选择学生和 dummyFiled,执行场景:

  1. manyToOne (defaultFetch -> eager),隐式 JOIN。 结果:完成所有工作的单个 SQL 查询。
  2. manyToOne (Fetch -> lazy),隐式 JOIN。 结果:与 1 相同的 SQL 查询。
  3. manyToOne (defaultFetch -> eager),显式 JOIN。 结果:与 1 相同的 SQL 查询。
  4. manyToOne(获取 -> 惰性),显式 JOIN。 结果:与 1 相同的 SQL 查询。

因此,在我的测试环境(EclipseLink,..)中,我有从我的 JPQL 查询生成的相同 SQL 查询。所以我可以说性能是一样的(当然,在我的测试条件中我再说一遍,我希望有人可以确认/纠正我的结果并制定一般规则。

【问题讨论】:

  • 这里您选择的是单个对象属性,这并不是真正的 JPQL 用例,当您选择整个对象模型时会变得很有趣——尤其是当它们嵌套多个层次时。然后可空状态、延迟获取和连接可以开始对查询和填充事物的方式以及最终将向数据库发起多少查询以完成工作产生影响。

标签: java jpa jpa-2.0 jpql


【解决方案1】:

通常,我总是明确每个JOIN。主要目标是更清楚地了解查询正在做什么以及如何做,并在查询未来发生变化时拥有更可预测的 SQL。

但我可以举一个例子,隐式 JOIN 比显式 JOIN 具有更多性能,即使这种性能增益在大多数情况下都非常小,而且就个人而言,不会承担风险(我稍后会解释什么风险)。

假设您尝试将所有图书归入同一出版商。使用 explicit JOIN,这是 JPQL:

 SELECT count(publisher.id) 
 FROM FROM Book b
 JOIN b.publisher publisher
 WHERE publisher.id = 1
 GROUP by publisher.id

此查询是可读的,没有任何重复,并且此查询中的任何更改(例如添加另一个WHERE 条件)将按预期维护生成的 SQL。

现在,使用 隐式 JOIN:

 SELECT count(b.publisher.id) 
 FROM FROM Book b
 WHERE b.publisher.id = 1
 GROUP by b.publisher.id

by b.publisher.id 重复了三遍。如果添加其他条件,则 Hibernate 的 JPQ 解释可能会更改生成的 SQL,执行不必要的 JOIN,如 Kayaman answer 中所述。

但是这个带有隐式 JOIN 的 JPQL 不会在生成的 SQL 上对publisher 表做额外的JOIN,因为他可以使用来自published_idid

 SELECT Count(b.published_id) AS col_0_0_ 
 FROM   Book b
 WHERE  b.published_id = ? 
 GROUP  BY b.published_id

以及带有额外JOIN 的 SQL 显式 JOIN:

 SELECT Count(published.id) AS col_0_0_ 
 FROM   Book b
 INNER JOIN published published1_ 
           ON b.published_id = published1_.id 
 WHERE  published_id = ? 
 GROUP  BY published_id

但这只有在publisher 的外键位于book 一侧时才会发生。在这种情况下是正确的方法,但是在某些映射中,外键可能位于表的任何一侧,因此显式和隐式 JPQL 查询可以生成相同的 SQL。

根据我的经验,显式 JOIN 始终是更好的选择。 Hibernate 并不总是理解在同一个查询上重复的同一个隐式 JOIN 是同一个 JOIN 的一部分,从而创建了未优化/奇怪的 SQL。

【讨论】:

  • 感谢您分享您的经验@Dherik。以后我会考虑的。
  • 有趣的见解。使用正确的 RDBMS,join elimination 将启动并消除数据库级别上不必要的连接。例如。在 Oracle、SQL Server、Db2 中,如果相关列上存在外键,则两个查询仍将以相同的方式执行。
【解决方案2】:

它们的解析方式不同,因此根据查询、实体关系和其他类似内容,它们最终可能会成为不同的 SQL 查询。理想情况下,只要 JPQL 查询做同样的事情,应该没有区别,但是 it doesn't always work that way

推荐的方法是使用显式连接,它还有其他优点,例如在惰性关系上指定 JOIN FETCH。这个问题过于关注性能,显然如果其中一个性能更高但结果相同,就没有理由使用速度较慢的一个。

启用 SQL 日志记录以查看生成的查询是验证您的应用程序是否正在执行您期望的查询的好方法,无论您使用哪种语法。您不能只依赖 JPQL,您需要了解并了解您的数据库,这样您就不仅仅是使用“混淆层”,因为 a_horse_with_no_name 喜欢调用 ORM 框架;)

【讨论】:

  • 在第 3 段中,您建议我启用 SQL 日志记录并查看。我已经这样做了,并且我已经更新了我的问题,如果你检查它会很好=)
  • @ziMtyth 但除了推荐显式连接之外没有一般规则。它们应该是等价的,但您必须始终检查实际查询(如果结果与预期不符,可能会提交错误报告)。
【解决方案3】:

它们应该是相同的,但实际上它主要取决于底层数据库。在当前环境中测试性能的一种快速方法是启用 SQL 日志记录,跟踪您的 jpql 被翻译成的本机查询,并直接使用 SQL 客户端尝试这些查询。

【讨论】:

  • 为什么要依赖数据库?如果 JPA 将它们都解析为同一个查询,则没有区别。所以问题是,JPA 是否总是将它们解析为相同的查询。
  • 因为您的 jpa 实现使用您定义的 SQL 方言来翻译这些查询。
  • 方言仍然是 JPA 实现的一部分,而不是数据库。一般来说,方言对 JPQL 也没什么好说的。在将查询转换为数据库特定语法时,它变得相关,此时连接等的一般结构应该已经完成​​。
  • 你是说同样的 jpql 查询在 Oracle db 和 MySQL db 上具有相同的性能(仅以这些为例)?如果您需要比较性能,您必须获取本地查询并在现场尝试这些查询。
  • 当然不是,我们不是在比较不同的数据库。我们正在比较这两个 JPQL 查询以及它们是否最终成为相同的 SQL 查询。这是由 JPA 实现决定的。如果您查看方言类的内容,您会注意到它们包含简单的语法映射,与连接或任何会影响查询结构的内容无关。所以我们又回到了起点;考虑到惰性映射等问题,JPA 是否总是为这两个 JPQL 查询创建相同的查询?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-04
  • 2010-09-09
  • 2012-08-04
  • 2013-06-30
  • 2012-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多