주요글: 도커 시작하기
반응형

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;
        }
    }
}

 

+ Recent posts