英文:
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); // 测试用途
Optional<IdentificationMaster> op2 = specRepo.findByIdentificationType(idType); // 测试用途
return specRepo.findById(idType)
.orElse(specRepo.findByIdentificationType(idType)
.orElseThrow(() -> new ResourceNotFoundException("Id Type Not Found " + idType)));
}
对于 idType,我们期望它可以是两个值之一,要么是主键id,要么是相应的identificationType。表格只有两列,id 和 identificationType。问题是,即使 op1 或 op2 不为空,它仍然会抛出 ResourceNotFoundException 异常。现在,如果我像这样更改返回值
return specRepo.findByIdentificationType(idType)
.orElse(specRepo.findById(idType)
.orElseThrow(() -> new ResourceNotFoundException("Id Type Not Found " + idType)));
它仍然会抛出相同的异常!
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;
// 获取器和设置器
}
问题可能是什么?
英文:
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?
答案1
得分: 6
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:
1. `specRepo.findByIdentificationType(idType)`
2. `orElse` cannot be executed as its argument is not evaluated yet
3. `specRepo.findById(idType)`
4. `.orElseThrow(() -> new ResourceNotFoundException("..."))`
5. The result of 3 and 4 becomes an object `o`
6. `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:
1. `specRepo` returns a non-empty Optional.
2. `specRepo` returns an empty object.
In scenario 1, `idType` is a valid `identificationType` and 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 an exception being thrown.
### Edit:
While this answer diagnoses the problem and describes what is the reason for such behavior, the answer by `@Abinash Ghosh` provides the simplest and in my opinion the best solution to the problem.
In general, avoid using `orElse`. In this case, add the `findByIdentificationTypeOrId(String it, String id)` to your repository.
英文:
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)orElsecannot be executed as it's argument is not evaluated yetspecRepo.findById(idType).orElseThrow(() -> new ResourceNotFoundException("..."))- The result of 3 and 4 becomes an object
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:
specReporeturns an non-empty Optional.specReporeturns 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.
Edit:
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.
答案2
得分: 3
@xenteros 是对的,那就是问题所在。
你可以使用 findByIdentificationTypeOrId 在一次查询中获取数据。
return specRepo.findByIdentificationTypeOrId(idType, idType)
.orElseThrow(() -> new ResourceNotFoundException("...")));
以及类似这样的 repository:
@Repository
public interface IdentificationSpecRepository extends CrudRepository<IdentificationMaster, String>{
Optional<IdentificationMaster> findByIdentificationTypeOrId(String identificationType, String id);
}
英文:
@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);
}
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论