JPA метод findBy всегда идет к orElseThrow - PullRequest
6 голосов
/ 06 апреля 2020

Это наш код

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 мы ожидаем два значения: идентификатор первичного ключа или соответствующее ему identificationType. Таблица содержит только два столбца id и identificationType. Проблема в том, что он выдает ResourceNotFoundException, даже если op1 или op2 не пусто. Теперь, если я изменю свое возвращение следующим образом

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

@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

}

В чем может быть проблема?

Ответы [ 2 ]

5 голосов
/ 06 апреля 2020
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("...")));

Он будет вызываться только при необходимости.

У нас есть два сценария ios здесь:

  1. specRepo возвращает непустое значение Необязательно.
  2. specRepo возвращает пустой объект.

В сценарии 1 idType является действительным identificationType, следовательно, не id, поэтому findById сгенерирует исключение. В сценарии 2 idType не является допустимым identificationType, и если оно является допустимым id, метод должен привести к исключению.

Редактировать:

В то время как этот ответ диагностирует проблема и описывает причину такого поведения, ответ @Abinash Ghosh обеспечивает самое простое и самое лучшее решение проблемы.

В общем, избегайте использования orElse. В этом случае добавьте findByIdentificationTypeOrId(String it, String id) в свой репозиторий.

3 голосов
/ 06 апреля 2020

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