๋๋ฉ์ธ ์ฃผ๋ ์ค๊ณ(DDD)๋ ๋ช
์ธ(SPECIFICATION)๋ผ๋ ๊ฐ๋
์ ์๊ฐํ๋๋ฐ, ์คํ๋ง ๋ฐ์ดํฐ JPA๋ JPA Criteria๋ก ์ด ๊ฐ๋
์ ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํ๋ค.
๋ช
์ธ๋ฅผ ์ดํดํ๊ธฐ ์ํ ํต์ฌ ๋จ์ด๋ ์ ์ด(predcate) ์ธ๋ฐ ๋จ์ํ ์ฐธ์ด๋ ๊ฑฐ์ง์ผ๋ก ํ๊ฐ๋๋ค. ๊ทธ๋ฆฌ๊ณ AND, OR ๊ฐ์ ์ฐ์ฐ์๋ก ์กฐํฉํ ์ ์๋ค.
์๋ฅผ ๋ค์ด ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ํ๊ธฐ ์ํ ์ ์ฝ ์กฐ๊ฑด ํ๋ํ๋๋ฅผ ์ ์ด๋ผ ํ ์ ์๋ค.
์คํ๋ง ๋ฐ์ดํฐ JPA๋ org.springframework.data.jpa.domain.Specification ํด๋์ค๋ก ์ ์ํ๋ค.
Specification์ ์ปดํฌ์งํธ ํจํด์ผ๋ก ๊ตฌ์ฑ๋์ด ์์ด ์ฌ๋ฌ Specification์ ์กฐํฉํ ์ ์๋ค.
๋ฐ๋ผ์ ๋ค์ํ ๊ฒ์์กฐ๊ฑด์ ์กฐ๋ฆฝํด ์๋ก์ด ๊ฒ์์กฐ๊ฑด์ ์ฝ๊ฒ ๋ง๋ค ์ ์๋ค.
๋ช ์ธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด JpaSpecificationExecutor ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ผ๋ฉด ๋๋ค.
//JpaSpecificationExecutor ์์public interface OrderRepository extends JpaRepository<Order, Long>, JpaSpecificationExecutor<Order>{...}
//JpaSpecificationExecutor ์ธํฐํ์ด์คpublic interface JpaSpecificationExecutor<T>{ T findOne(Specification<T> spec); List<T> findAll(Specification<T> spec); Page<T> findAll(Specification<T> spec, Pageable pageable); List<T> findAll(Specification<T> spec, Sort sort); long count(Specification<T> spec);}
JpaSpecificationExecutor์ ๋ฉ์๋๋ค์ Specification์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์ ๊ฒ์ ์กฐ๊ฑด์ผ๋ก ์ฌ์ฉํ๋ค.
//๋ช
์ธ ์ฌ์ฉ ์ฝ๋import static org.springframework.data.jpa.domain.Specifications.*; //whereimport static org.tmkim.jpashop.domain.spec.OrderSpec.*;public List<Order> findOrders(String name){ List<Order> result = orderRepository.findAll(where(memberName(name)).and(isOrderStatus()));}
Specification๋ ๋ช
์ธ๋ค์ ์กฐ๋ฆฝํ ์ ์๋๋ก ๋์์ฃผ๋ ํด๋์ค๋ค.(where(), and(), or(), not() ๋ฉ์๋๋ฅผ ์ ๊ณต)
findAll()์ ๋ณด๋ฉด ํ์ ์ด๋ฆ ๋ช
์ธ(memberName)์ ์ฃผ๋ฌธ ์ํ ๋ช
์ธ(isOrderStatus)๋ฅผ and๋ก ์กฐํฉํด์ ๊ฒ์ ์กฐ๊ฑด์ผ๋ก ์ฌ์ฉํ๋ค.
๋ช
์ธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ๋ import static์ ์ ์ฉํ๋ฉด ์ฝ๊ธฐ ์ฌ์ด ์ฝ๋๊ฐ ๋๋ค.
//OrderSpec ๋ช
์ธ ์ ์ ์ฝ๋public class OrderSpec{ public static Specification<Order> memberName(final String membeName){ return new Specification<Order>(){ public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> query, CriteriaBuilder builder){ if(StringUtils.isEmpty(memberName)) return null; Join<Order, Member> m = root.join("member", JoinType.INNER); //ํ์๊ณผ ์กฐ์ธ return builder.equal(m.get("name"), memberName); } } } public static Specification<Order> isOrderStatus(){ return new Specification<Order>(){ public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> query, CriteriaBuilder builder){ return builder.equal(root.get("status"), OrderStatus.ORDER); } } }}
๋ช
์ธ๋ฅผ ์ ์ํ๋ ค๋ฉด Specification ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ฉด ๋๋ค.(์ฌ๊ธฐ์ ํธ์์ ๋ฌด๋ช
ํด๋์ค ์ฌ์ฉ)
๋ช
์ธ๋ฅผ ์ ์ํ ๋๋ toPredicate(...) ๋ฉ์๋๋ง ๊ตฌํํ๋ฉด ๋๋๋ฐ JPA Criteria์ Root, CriteriaQuery, CriteriaBuilder ํด๋์ค๊ฐ ํ๋ผ๋ฏธํฐ๋ก ์ฃผ์ด์ง๋๋ฐ,
์ด ํ๋ผ๋ฏธํฐ๋ค์ ํ์ฉํด์ ์ ์ ํ ๊ฒ์ ์กฐ๊ฑด์ ๋ฐํํ๋ฉด ๋๋ค.