JPA findBy 方法总是转到 orElseThrow

JPA findBy method always goes to orElseThrow

这是我们的代码

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)));
}

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

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

再次抛出相同的异常!

存储库

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

    Optional<IdentificationMaster> findByIdentificationType(String identificationType);
}

实体

@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

}

可能是什么问题?

return specRepo.findByIdentificationType(idType)
            .orElse(specRepo.findById(idType)
                .orElseThrow(() -> new ResourceNotFoundException("...")));

是这个原因。

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("...")));

只有在需要的时候才会调用。

这里有两种情况:

  1. specRepo returns 非空可选。
  2. specRepo returns 空对象。

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

编辑:

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

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

@xenteros 是对的,这就是问题所在。 您可以使用 findByIdentificationTypeOrId 在一个查询中获取数据

return specRepo.findByIdentifcationTypeOrId(idType, idType)
                .orElseThrow(() -> new ResourceNotFoundException("...")));

和类似的存储库

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

    Optional<IdentificationMaster> findByIdentificationTypeOrId(String identificationType, String id);
}