一光年

[Spring-JPA] 使用findOne出现IncorrectResultSizeDataAccessException错误

2019.06.17

在使用JPA时repository通常会继承JpaRepository,此接口由继承于接口类QueryByExampleExecutor。其中,有一个方法名为:findOne。

  	/**
	 * Returns a single entity matching the given {@link Example} or {@literal null} if none was found.
	 *
	 * @param example must not be {@literal null}.
	 * @return a single entity matching the given {@link Example} or {@link Optional#empty()} if none was found.
	 * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if the Example yields more than one result.
	 */
	<S extends T> Optional<S> findOne(Example<S> example);

如果只看方法名,有的开发者会想当然的认为该方法返回一个符合查询条件的对象,即使符合条件的对象有多个。

然而实际情况是,当出现多个查询结果对象时,会抛出IncorrectResultSizeDataAccessException异常。

回想使用findOne方法最初的目的,其实是:

我不知道ID,但通过复合条件想取得唯一一条记录

但调用过程却忽略了一个前提,即:

如果因为数据错误等原因,数据库返回了多个对象,如果没有排序该怎么返回呢?

那么,这个接口是否可以考虑设计如下方法呢?

  <S extends T> Optional<S> findOne(Example<S> example, Sort sort);

答案是多此一举。因为这个接口的实现,其实就是findAll调用并取得第一条的实现。这种做法,和我们调用findOne最初的目的其实是不一样的。

如果要调用findOne,还要确保不抛出异常的话,还需要在这之前调用count方法才行。

  if (repo.count(example) == 1) {
    XXX data = repo.findOne(example);
  }

这样的写法比起取得列表方式来讲,稍微简洁一些,但查询效率上讲就低了一些,毕竟查询了两次。

  List<XXX> list = repo.findAll(example);
  if (list.size() > 0) {
    XXX data = list.get(0);
  }

这个问题,恐怕就是代码可读性和运行效率的权衡了。