CrudRepository findOne() 和 JpaRepository getOne() 的区别

Posted

技术标签:

【中文标题】CrudRepository findOne() 和 JpaRepository getOne() 的区别【英文标题】:Difference between CrudRepository findOne() and JpaRepository getOne() 【发布时间】:2016-01-18 01:21:34 【问题描述】:

我读到getOne() 是延迟加载的,findOne() 会立即获取整个实体。我检查了调试日志,甚至在我的 sql 服务器上启用了监视以查看执行了哪些语句,我发现 getOne()findOne() 都生成并执行相同的查询。但是,当我使用getOne() 时,这些值最初为空(当然,id 除外)。

那么谁能告诉我,如果这两种方法都在数据库上执行相同的查询,我为什么要使用一种而不是另一种呢?我基本上是在寻找一种在不获取其所有子项/属性的情况下获取实体的方法。

EDIT1:

Entity code

道码:

@Repository
public interface FlightDao extends JpaRepository<Flight, Long> 

Debugging log findOne() vs getOne()

EDIT2:

感谢 Chlebik,我能够确定问题所在。正如 Chlebik 所说,如果您尝试访问由getOne() 获取的实体的任何属性,则将执行完整的查询。在我的情况下,我在调试时检查行为,一次移动一行,我完全忘记了在调试时 IDE 尝试访问对象属性以进行调试(或者至少这是我认为正在发生的事情),所以调试触发器完整的查询执行。我停止调试,然后检查日志,一切正常。

getOne() vs findOne()(此日志取自 mysql general_log 而不是休眠。

Debugging log

No debugging log

【问题讨论】:

【参考方案1】:

这只是一个猜测,但在“纯 JPA”中有一个名为 getReference 的 EntityManager 方法。它旨在检索其中只有 ID 的实体。它的用途主要是表示存在参考,而不需要检索整个实体。也许代码会告诉更多:

// em is EntityManager
Department dept = em.getReference(Department.class, 30);  // Gets only     entity with ID property, rest is null
Employee emp = new Employee();
emp.setId(53);
emp.setName("Peter");
emp.setDepartment(dept);
dept.getEmployees().add(emp);
em.persist(emp);

我假设 getOne 具有相同的目的。为什么生成的查询与您所问的相同?好吧,JPA 圣经中的 AFAIR - Mike Keith 和 Merrick Schincariol 的Pro JPA2 - 几乎每个段落都包含“行为取决于供应商”之类的内容。

编辑:

我已经设置了自己的设置。最后我得出的结论是,如果您以任何方式干扰使用 getOne 获取的实体(甚至使用 entity.getId()),它会导致 SQL 被执行。尽管如果您仅使用它来创建代理(例如,用于上面代码中所示的关系指示符),则不会发生任何事情并且不会执行额外的 SQL。所以我假设在你的服务类中你对这个实体做了一些事情(使用 getter,记录一些东西),这就是这两种方法的输出看起来相同的原因。

ChlebikGitHub with example code SO helpful question #1 SO helpful question #2

【讨论】:

感谢您的信息。实际上getOne() 所做的是调用getReference()。我真的很困惑,因为正如我之前所说,这两种方法都生成并执行了相同的查询!所以也许它与你所说的 MySQL 有关?我真的希望有人能证实这一点。您是否知道在正常情况下两种方法之间的查询是否应该有所不同? 供应商并不意味着例如。 MySQL 但 Hibernate/TopLink/无论你使用什么。我假设正在执行的查询基于您的实体中定义的关系,并且可能由 JPA 实现进行优化。恕我直言,对于简单实体,将处理程序获取到实体(如使用 getReference)与实际检索所有数据之间没有太大区别。 我必须得到一个参考,因为我尝试获取的实体有很多子实体,当我只需要实体的 ID 时,要获取整个东西是很多工作。让我们等一会儿,也许有人可以告诉我们发生了什么。再次感谢 请发布您的实体代码。有几种可能性可以想到,但没有代码我什么也说不出来。 检查我的第二次编辑。我真的不能足够支持你..我现在觉得很愚蠢:-|非常感谢 Chlebik :)【参考方案2】:

假设您想通过 id 删除 Entity。在SQL 中,您可以执行如下查询:

"delete form TABLE_NAME where id = ?". 

Hibernate 中,首先您必须获取Entity 的托管实例,然后将其传递给EntityManager.remove 方法。

Entity a = em.find(Entity.class, id);
em.remove(a);

但是这样,您必须在删除之前从数据库中获取要删除的Entity。真的有必要吗?

方法EntityManager.getReference 返回一个Hibernate 代理而不查询数据库和设置实体的属性。除非您尝试自己获取返回的代理的属性。

方法JpaRepository.getOne 使用EntityManager.getReference 方法而不是EntityManager.find 方法。所以每当你需要一个托管对象但你并不真的需要为此查询数据库时,最好使用JpaRepostory.getOne 方法来消除不必要的查询。

【讨论】:

【参考方案3】:

如果在特定 ID 的表中找不到数据,findOne 将返回 null,而 getOne 将抛出 javax.persistence.EntityNotFoundException。 两者都有自己的优点和缺点。请看下面的例子:

    如果未找到数据对您来说不是失败案例(例如,您只是 验证数据是否被删除,成功将是要被删除的数据 null),您可以使用 findOne。 在另一种情况下,您可以使用 getOne。

如果您知道结果,可以根据您的要求进行更新。

【讨论】:

以上是关于CrudRepository findOne() 和 JpaRepository getOne() 的区别的主要内容,如果未能解决你的问题,请参考以下文章

CrudRepository findOne()和JpaRepository getOne()之间的区别

知识库-简单理解CrudRepository接口中的方法

如何在 Spring Data JPA CRUDRepository 中添加缓存功能

CrudRepository:由多个相关实体查找

CrudRepository:返回一个结果,按列排序[重复]

JpaRepository vs CRUDRepository findAll