스프링 데이터 JPA 예제와 트레이드 오프
스프링 데이터 JPA 예제 클래스 의존 관계
- ItemSerive가 ItemRepository 인터페이스에 의존
- ItemRepository를 JpaItemRepositoryV2가 구현
- JpaItemRepositoryV2가 SpringDataJpaItemRepository에 의존
- SpringDataJpaItemRepository 인터페이스는 JpaRepository 인터페이스를 상속
- SpringDataJpaItem 프록시 리포지토리가 SpringDataJpaItemRepository를 구현
중간에서 JpaItemRepository가 어댑터 역할을 해준 덕분에 itemService가 사용하는 itemRepository 인터페이스를 그대로 사용할 수 있고, 클라이언트인 itemService의 코드를 변경하지 않아도 된다.
하지만.
- 구조를 맞추기 위해서, 중간에 어댑터를 두기 때문에 전체 구조가 복잡해지고, 사용하는 클래스도 많아지는 단점이 있다.
- 코드를 구현하는 개발자 입장에서 보면 중간에 어댑터와 실제 코드도 만들어야 하는 불편함이 있다.
- 유지보수 관점에서 ItemService를 변경하지 않고, ItemRepository의 구현체를 변경하여 DI, OCP 원칙을 지킬 수 지만, 구조가 복잡해지면서 어댑터 코드와 실제 코드까지 유지보수 해야하는 어려움이 발생한다.
그래서 itemService에서 직접 SpringDataJpaItemRepository를 사용하여 DI와 OCP 원칙을 포기하고, 구조를 단순하게 가져가는 방법을 선택할 수도 있다. 구현체를 바꿔야할 때는 itemService를 직접 손을 대야 한다.
이렇게 두가지 방식에서 트레이드 오프를 가진다. 둘 중 하나만 정답이 아니라, 상황에 따라 사용해야 하는 방법이 다르다.
추상화에도 비용이 든다. 추상화 비용을 넘어설 만큼 효과가 있을 때 추상화를 도입하는 것이 좋다.
실용적인 구조
이전에 Querydsl을 사용한 ItemRepository 구현체에서 스프링 데이터 JPA를 사용하지 않는 아쉬움이 있었다.
이번에는 스프링 데이터 JPA와 Querydsl을 함께 사용할 수있는 구조를 만든다.\
ItemService를 스프링 데이터 JPA를 사용하는 ItemRepositoryV2와 Querydsl을 사용하는 ItemQueryRepositoryV2에 의존하도록 만든다. 이렇게해서 기본적인 CRUD는 스프링 데이터 JPA가 담당하고, 복잡한 조회 쿼리는 Querydsl이 담당한다.
ItemServiceV2
@Service
@RequiredArgsConstructor
@Transactional
public class ItemServiceV2 implements ItemService{
private final ItemRepositoryV2 itemRepositoryV2;
private final ItemQueryRepositoryV2 itemQueryRepositoryV2;
@Override
public Item save(Item item) {
return itemRepositoryV2.save(item);
}
@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
Item findItem = itemRepositoryV2.findById(itemId).orElseThrow();
findItem.setItemName(updateParam.getItemName());
findItem.setPrice(updateParam.getPrice());
findItem.setQuantity(updateParam.getQuantity());
}
@Override
public Optional<Item> findById(Long id) {
return itemRepositoryV2.findById(id);
}
@Override
public List<Item> findItems(ItemSearchCond itemSearch) {
return itemQueryRepositoryV2.findAll(itemSearch);
}
}
ItemRepositoryV2
public interface ItemRepositoryV2 extends JpaRepository<Item, Long> {
//기본적인 CRUD 제공
}
ItemQueryRepositoryV2
@Repository
public class ItemQueryRepositoryV2 {
private final JPAQueryFactory query;
public ItemQueryRepositoryV2(EntityManager em) {
this.query = new JPAQueryFactory(em);
}
public List<Item> findAll(ItemSearchCond cond) {
return query
.select(item)
.from(item)
.where(
likeItemName(cond.getItemName()),
maxPrice(cond.getMaxPrice())
)
.fetch();
}
private BooleanExpression maxPrice(Integer maxPrice) {
if (maxPrice != null) {
return item.price.loe(maxPrice); //loe : 작거나 같다
}
return null;
}
private BooleanExpression likeItemName(String itemName) {
if(StringUtils.hasText(itemName)) {
return item.itemName.like("%"+itemName+"%");
}
return null;
}
}
- ItemServiceV2는 스프링 데이터 JPA를 제공하는 ItemRepositoryV2도 참조하고, Querydsl과 관련된 ItemQueryRepositoryV2도 직접 참조한다.
- ItemRepositoryV2를 통해서 스프링 데이터 JPA 기능으로 기본적인 CRUD를 구현하고, ItemQueryRepositoryV2를 통해서 복잡한 조회 쿼리를 Querydsl로 해결할 수 있다.
'Spring' 카테고리의 다른 글
[Spring DB2] 스프링 트랜잭션 이해 - 2 (0) | 2024.03.28 |
---|---|
[Spring DB2] 스프링 트랜잭션 이해 - 1 (2) | 2024.03.27 |
[Spring DB2] 데이터 접근 기술 - Querydsl (0) | 2024.03.25 |
[Spring DB2] 데이터 접근 기술 - 스프링 데이터 JPA (0) | 2024.03.21 |
[Spring DB2] 데이터 접근 기술 - JPA (0) | 2024.03.19 |