반응형
Spring Data Jpa의 Speicfication을 애용하는 편인데, 이 Specification을 사용해서 조건을 조합하다보면 다음과 같은 코드를 종종 작성하게 된다. (관련 내용은 http://javacan.tistory.com/entry/SpringDataJPA-Specifcation-Usage 참고)
Specifications<Check> specs = Specifications.where(
CheckSpecs.yearQuarter(searchRequest.getYear(), searchRequest.getQuarter()));
if (searchRequest.hasTeamCd())
specs = specs.and(CheckSpecs.teamCd(searchRequest.getTeamCd()));
if (searchRequest.hasPlanDate())
specs = specs.and(CheckSpecs.planDate(searchRequest.getPlanDate()));
List<Check> checks = checkRepository.findAll(specs);
if 절과 각 Spec을 and로 엮는 코드가 실수하기 좋게 되어 있다. 이를 보완하고자 SpecBuilder라는 보조 클래스를 하나 만들었다. 이 클래스를 사용하면 위 코드를 다음과 같이 변경할 수 있다.
Specification<Check> spec = SpecBuilder.builder(Check.class)
.and(CheckSpecs.yearQuarter(searchRequest.getYear(), searchRequest.getQuarter()))
.whenHasText(searchRequest.getTeamCd(), str -> CheckSpec.teamCd(str))
.whenHasText(searchRequest.getPlanDate(), CheckSpec::planDate)
.toSpec();
.List<Check> checks = checkRepository.findAll(specs);
단순히 and로 조합하는 경우, if를 사용할 때보다 코드를 보기가 더 좋아졌다.
SpecBuilder의 완전한 코드는 다음과 같다.
public class SpecBuilder {
public static <T> Builder<T> builder(Class<T> type) {
return new Builder<T>();
}
public static class Builder<T> {
private List<Specification<T>> specs = new ArrayList<>();
private void addSpec(Specification<T> spec) {
if (spec != null) {
specs.add(spec);
}
}
public Builder<T> and(Specification<T> spec) {
addSpec(spec);
return this;
}
public Builder<T> ifHasText(String str,
Function<String, Specification<T>> specSupplier) {
if (StringUtils.hasText(str)) {
addSpec(specSupplier.apply(str));
}
return this;
}
public Builder<T> ifTrue(Boolean cond,
Supplier<Specification<T>> specSupplier) {
if (cond != null && cond.booleanValue()) {
addSpec(specSupplier.get());
}
return this;
}
public <V> Builder<T> ifNotNull(V value,
Function<V, Specification<T>> specSupplier) {
if (value != null) {
addSpec(specSupplier.apply(value));
}
return this;
}
public Specification<T> toSpec() {
Specification<T> spec = Specification.where(null);
for (Specification<T> s : specs) {
spec = spec.and(s);
}
return spec;
}
}
}