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?
Its findById method retrieves an entity by its id. The return value is Optional<T> . Optional<T> is a container object which may or may not contain a non-null value. If a value is present, isPresent returns true and get returns the value.
The orElseThrow method will return the value present in the Optional object. If the value is not present, then the supplier function passed as an argument is executed and an exception created on the function is thrown.
According with the getOne API: Returns a reference to the entity with the given identifier. According with the findOne API: Retrieves an entity by its id.
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.
@xenteros is right that is the problem.
You can use findByIdentificationTypeOrId
to get data in one query
return specRepo.findByIdentifcationTypeOrId(idType, idType)
.orElseThrow(() -> new ResourceNotFoundException("...")));
and repository like
@Repository
public interface IdentificationSpecRepository extends CrudRepository<IdentificationMaster, String>{
Optional<IdentificationMaster> findByIdentificationTypeOrId(String identificationType, String id);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With