JPA findBy 方法总是转到 orElseThrow

Aru*_*ran 6 java optional findby spring-data-jpa spring-boot

这是我们的代码

private IdentificationMaster validateIdentificationType(String idType) {
    if(!StringUtils.isNotBlank(idType))
        throw new IllegalArgumentException("Invalid idType");

    Optional<IdentificationMaster> op1 = specRepo.findById(idType); //testing purpose

    Optional<IdentificationMaster> op2 = specRepo.findByIdentificationType(idType); //testing purpose

    return specRepo.findById(idType)
            .orElse(specRepo.findByIdentificationType(idType)
                    .orElseThrow(() -> new ResourceNotFoundException("Id Type Not Found " + idType)));
}
Run Code Online (Sandbox Code Playgroud)

因为idType我们期待两个值,它可以是主键 id或其对应的identificationType. 表只有两列ididentificationType。问题是ResourceNotFoundException即使op1op2不为空它也会抛出。现在如果我像这样改变我的回报

return specRepo.findByIdentificationType(idType)
            .orElse(specRepo.findById(idType)
                .orElseThrow(() -> new ResourceNotFoundException("Id Type Not Found " + idType)));
Run Code Online (Sandbox Code Playgroud)

它再次抛出相同的异常!

存储库

@Repository
public interface IdentificationSpecRepository extends CrudRepository<IdentificationMaster, String>{

    Optional<IdentificationMaster> findByIdentificationType(String identificationType);
}
Run Code Online (Sandbox Code Playgroud)

实体

@Entity
@Table(name = "IDENTIFICATION_MASTER")
public class IdentificationMaster {

    @Id
    @Column(name = "ID")
    private String id;


    @Column(name = "IDENTIFICATION_TYPE", unique = true)
    private String identificationType;

    // getters and setters

}
Run Code Online (Sandbox Code Playgroud)

可能是什么问题呢?

xen*_*ros 6

return specRepo.findByIdentificationType(idType)
            .orElse(specRepo.findById(idType)
                .orElseThrow(() -> new ResourceNotFoundException("...")));
Run Code Online (Sandbox Code Playgroud)

是原因。

Java 非常渴望执行并且总是调用 orElse 方法来准备以防万一它需要它。

你的执行顺序是:

  1. specRepo.findByIdentificationType(idType)
  2. orElse 无法执行,因为它的参数尚未评估
  3. specRepo.findById(idType)
  4. .orElseThrow(() -> new ResourceNotFoundException("..."))
  5. 3和4的结果变成一个对象 o
  6. orElse(o)

而不是使用orElse一个应该更喜欢orElseGet

return specRepo.findByIdentificationType(idType)
            .orElseGet(() -> specRepo.findById(idType)
                .orElseThrow(() -> new ResourceNotFoundException("...")));
Run Code Online (Sandbox Code Playgroud)

它只会在需要时调用。

我们这里有两个场景:

  1. specRepo 返回一个非空的 Optional。
  2. specRepo 返回空对象。

在场景 1 中,idTypeis a valididentificationType因此不是 an id,因此findById将抛出异常。在场景 2 中,idType是无效的identificationType,如果它是合法的,则id该方法应导致抛出异常。

编辑:

虽然这个答案诊断了问题并描述了这种行为的原因,但@Abinash Ghosh答案提供了问题的最简单和最好的解决方案。

一般来说,避免使用orElse. 在这种情况下,将 添加findByIdentificationTypeOrId(String it, String id)到您的存储库。