본문 바로가기
BackEnd

Java Service Tree Framework 의 Global Tree Map 설명

by 313devgrp 2023. 7. 24.

JPA Specification

검색 조건을 추상화하기 위해 사용된다.

이를 사용하기 위해 Repository에서 JpaSpecificationExecutor를 상속받아야 한다.

import com.mad.treemap.model.GlobalTreeMapEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface GlobalTreeMapJpaRepository extends JpaRepository<GlobalTreeMapEntity,Long>,JpaSpecificationExecutor<GlobalTreeMapEntity> {

}

JpaSpecificationExecutor 인터페이스를 살펴보면 다음과 같다.

/**
 * Interface to allow execution of {@link Specification}s based on the JPA criteria API.
 *
 * @author Oliver Gierke
 * @author Christoph Strobl
 */
public interface JpaSpecificationExecutor<T> {

    /**
     * Returns a single entity matching the given {@link Specification} or {@link Optional#empty()} if none found.
     *
     * @param spec can be {@literal null}.
     * @return never {@literal null}.
     * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one entity found.
     */
    Optional<T> findOne(@Nullable Specification<T> spec);

    /**
     * Returns all entities matching the given {@link Specification}.
     *
     * @param spec can be {@literal null}.
     * @return never {@literal null}.
     */
    List<T> findAll(@Nullable Specification<T> spec);

    /**
     * Returns a {@link Page} of entities matching the given {@link Specification}.
     *
     * @param spec can be {@literal null}.
     * @param pageable must not be {@literal null}.
     * @return never {@literal null}.
     */
    Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);

    /**
     * Returns all entities matching the given {@link Specification} and {@link Sort}.
     *
     * @param spec can be {@literal null}.
     * @param sort must not be {@literal null}.
     * @return never {@literal null}.
     */
    List<T> findAll(@Nullable Specification<T> spec, Sort sort);

    /**
     * Returns the number of instances that the given {@link Specification} will return.
     *
     * @param spec the {@link Specification} to count instances for. Can be {@literal null}.
     * @return the number of instances.
     */
    long count(@Nullable Specification<T> spec);
}

위의 메소드를 상속받아 사용할 수 있고, 매개변수로 Specification 객체를 넣어주면 된다.

 

Specification

public interface Specification<T> {
  Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
}

Specification 명세를 정의하고 조건쿼리를 생성하기 위해 

Specification 인터페이스의 toPredicate() 메소드를 구현해야 한다.

@Override
    public List<GlobalTreeMapEntity> findAllBy(GlobalTreeMapEntity globalTreeMapEntity) {

        Specification<GlobalTreeMapEntity> searchWith = (root, query, builder) -> builder.and(
                ReflectionUtils.getAllFields(globalTreeMapEntity.getClass()).stream()
                        .filter(
                                field -> {
                                    try {
                                        field.setAccessible(true);
                                        return !ObjectUtils.isEmpty(field.get(globalTreeMapEntity))
                                                && !"map_key".equals(field.getName());
                                    } catch (IllegalAccessException e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                        )
                        .map(
                                field -> {
                                    try {
                                        field.setAccessible(true);
                                        return builder.equal(root.get(field.getName()), field.get(globalTreeMapEntity));
                                    } catch (IllegalAccessException e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                        ).toArray(Predicate[]::new)
        );

        return globalTreeMapRepository.findAllBy(searchWith).stream()
                .collect(Collectors.toUnmodifiableList());

    }

보통은 Specification 명세를 정의하고 조건쿼리를 생성하기 위해 Specification 인터페이스의 toPredicate() 메소드를 구현해야 한다.

그리고 Specification 에서 Predicate 객체를 반환하여 Repository 에서 처리하는 방식인데

여기선, 직접 Predicate 객체를 build 하여 리턴하는 형태로 압축되었다.

( 해당 람다식은 Specification의 toPredicate() 메소드를 구현한 것입니다. )