Java 为什么Spring数据存储库上的getOne(…;)不会抛出EntityNotFoundException?

Java 为什么Spring数据存储库上的getOne(…;)不会抛出EntityNotFoundException?,java,spring,jpa,spring-data,spring-data-jpa,Java,Spring,Jpa,Spring Data,Spring Data Jpa,我正在处理一个奇怪的问题,我在做集成测试,调用我的控制器从不存在的数据库中获取一个对象 public Optional<T> get(Long id) { try { return Optional.ofNullable(repository.getOne(id)); } catch(EntityNotFoundException e) { return Optional.empty(); } } public可选get(长id){ 试一试{ 返回可选

我正在处理一个奇怪的问题,我在做集成测试,调用我的控制器从不存在的数据库中获取一个对象

public Optional<T> get(Long id) {

  try {
    return Optional.ofNullable(repository.getOne(id));
  } catch(EntityNotFoundException e) {
    return Optional.empty();
  }
}
public可选get(长id){
试一试{
返回可选的.ofNullable(repository.getOne(id));
}捕获(EntityNotFounde异常){
返回可选的.empty();
}
}
getOne(…)
找不到任何东西时,我希望出现
EntityNotFoundException
但实际上什么都没有。如果我检查我的结果,我可以看到我有一个空实体,它有一个指向它的处理程序链接“throwd
EntityNotFoundException
”,但我们没有进入catch,我返回了这个奇怪实体的可选值


我无法理解这种行为。

这是由于JPA指定
EntityManager.getReference(…)
工作的方式造成的。它应该返回一个代理,该代理将解析第一次访问属性时要返回的对象,或者最终抛出包含的异常

解决此问题的最简单方法是简单地使用
findOne(…)
,如
Optional.ofNullable(repository.findOne(…)
<如果找不到结果,code>findOne(…)将返回
null

解决此问题的更高级方法是使存储库直接返回
可选的
实例。这可以通过使用
Optional
作为
find…
-方法的返回类型来创建自定义基本存储库接口来实现

interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> {

  Optional<T> findOne(ID id);

  // declare additional methods if needed
}

interface YourRepository extends BaseRepository<DomainClass, Long> { … }
接口BaseRepository扩展了存储库{
可选findOne(ID);
//如果需要,声明其他方法
}
接口YourRepository扩展BaseRepository{…}

在Spring
@Repository
类中的。

中可以找到一个完整的例子,
getOne(id)
方法在查询对象之前(通过调用
entity.getId()
或其他方法)并不总是验证是否存在,因此不会延迟此类实体异常。要立即进行验证,请改用
findById(id)
(它返回一个
可选的
,如果具有该id的实体不存在,该属性将为空)


这就是我的工作方式

public User findUserById(Long id) {
    return userRepository.findById(id).orElse(null);
}

你为什么期待一个例外?javadoc没有提到异常。这是对不存在的数据调用getOne()时的典型JPA行为。此外,我可以看到此异常被抛出,但通过spring数据以某种方式处理?看起来是个好主意!我会尝试一下,但是。。。性能怎么样,findOne和getOne一样好吗?确实有区别,因为
getOne(…)
不会立即具体化对象,而
findOne(…)
会。前者的缺点是它也会延迟查找失败,这会让您感到痛苦。另外,
findOne(…)
将命中
EntityManager
中的一级缓存,因此在特定会话中,不会两次命中数据库。如果这真的成为一个问题,你可能想显式地插入缓存,但我会延迟,直到测量结果真正显示,这是一个问题。值得注意的是,到2019年,
crudepository
已经带来了
可选的findById(ID)这与2015年的答案基本相同。。。