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()之间的区别
如何在 Spring Data JPA CRUDRepository 中添加缓存功能