英文:
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)
orElse
cannot 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:
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.
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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论