This is our code
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)));
}
For idType
we're expecting two values it can either be primary key id or its corresponding identificationType
. Table only has two columns id
and identificationType
. The problem is that it throws ResourceNotFoundException
even if op1
or op2
is not empty. Now if I change my return like this
return specRepo.findByIdentificationType(idType)
.orElse(specRepo.findById(idType)
.orElseThrow(() -> new ResourceNotFoundException("Id Type Not Found " + idType)));
Its again throwing the same exception!
Repository
@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
}
What could be the problem?
return specRepo.findByIdentificationType(idType)
.orElse(specRepo.findById(idType)
.orElseThrow(() -> new ResourceNotFoundException("...")));
Is the reason.
Java is quite eager in execution and always calls the orElse method to prepare just in case it would need it.
The order of your execution is somehow:
specRepo.findByIdentificationType(idType)
orElse
cannot be executed as it's argument is not evaluated yetspecRepo.findById(idType)
.orElseThrow(() -> new ResourceNotFoundException("..."))
o
orElse(o)
Instead of using orElse
one should prefer orElseGet
.
return specRepo.findByIdentificationType(idType)
.orElseGet(() -> specRepo.findById(idType)
.orElseThrow(() -> new ResourceNotFoundException("...")));
It will only be called when needed.
We have two scenarios here:
specRepo
returns an non-empty Optional.specRepo
returns empty object.In scenario 1, idType
is a valid identificationType
thus is not an id
, so the findById
will throw an exception.
In scenario 2, idType
is not a valid identificationType
and if it is a legal id
the method should result in exception being thrown.
While this answers diagnoses the problem and describes what is the reason of such behavior, @Abinash Ghosh
answer provides the simplest and imo best solution of the problem.
In general, avoid using orElse
. In this case, add the findByIdentificationTypeOrId(String it, String id)
to your repository.