Spring

[Spring DB2] 데이터 접근 기술 - 스프링 데이터 JPA

gogi masidda 2024. 3. 21. 21:12

스프링 데이터 JPA 주요 기능

  • 공통 인터페이스 기능
    • JPARepository 인터페이스를 통해서 기본적인 CRUD 기능을 제공
    • 공통화 가능한 기능이 거의 모두 포함되어 있음.
  • 쿼리 메서드 기능 
    • 인터페이스에 메서드만 적어두면, 메서드 이름을 분석해서 필요한 JPQL을 만들고 실행해준다. JPQL은 JPA가 SQL로 번역해서 실행한다. 
    • 규칙
      • 조회 : find..By, read..By, query..By, get..By
        • ex) findByUsernameAndAgeGreaterThan(String username, int age)
      • COUNT: count..By / 반환 타입은 long
      • EXIST: exists..By / 반환 타입은 boolean
      • 삭제 : delete.. By, remove..By / 반환 타입은 long
      • DISTINCT: findDistinct, findMemberDistinctBy
      • LIMIT : findFirst3, findFirst, findTop, findTop3
    • 쿼리 메서드 기능 대신에 직접 @Query를 이용해서 JPQL을 사용할 수 있다.

사용법

public interface ItemRepository extends JpaRepository<Member, Long> {
}

JpaRepository를 상속받고, 제네릭에 관여할 <엔티티, 엔티티 ID>를 주면 된다. 이렇게 하면 구현 클래스 없이도 기본 CRUD 기능을 사용할 수 있다.


스프링 데이터 JPA 적용

public interface SpringDataJpaItemRepository extends JpaRepository<Item, Long> {
    //아래처럼 조건이 있는 거만 적으면 됨. 조건이 없는건 JpaRepository에 findAll()이 있음.
    List<Item> findByItemNameLike(String itemName);

    List<Item> findByPriceLessThanEqual(Integer price);

    //쿼리 메서드 (아래 메서드와 같은 기능 수행)
    List<Item> findByItemNameLikeAndPriceLessThanEqual(String itemName, Integer price);

    //쿼리 직접 실행
    @Query("select i from Item i where i.itemName like :itemName and i.price <= :price")
    List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);
}
  • 스프링 데이터 JPA가 제공하는 JpaRepository 인터페이스를 인터페이스 상속받으면 기본적인 CRUD 기능을 사용할 수 있다.
  • 공통적인 CRUD 기능이 아닌 조건이 있는, 이름으로 검색하거나, 가격 제한을 두고 검색하는 기능은 공통으로 제공하지 않아서 쿼리 메서드 기능을 사용하거나, 직접 @Query를 사용해서 JPQL을 작성하면 된다. 
  • 스프링 데이터 JPA 역시 동적쿼리에 약하다. 이것은 나중에 배우게되는 Querydsl을 사용하면 좋다. 
  • findItems의 @Query를 이용한 것처럼 쿼리를 직접 실행할 때는 파라미터를 명시적으로 바인딩해야 한다. 파라미터 바인딩은 @Param("itemName")처럼 애노테이션을 사용하고, 애노테이션의 값에 파라미터 이름을 주면 된다. 

ItemService의 코드를 변경하여 바로 SpringDataJpaItemRepository에 의존하도록 할 수 있지만, 코드를 그대로 유지하기 위해서 ItemRepository를 상속받는 JpaRepositoryV2를 만들었다.

@Repository
@Transactional
@RequiredArgsConstructor
public class JpaItemRepositoryV2 implements ItemRepository {

    private final SpringDataJpaItemRepository repository;

    @Override
    public Item save(Item item) {
        return repository.save(item);
    }

    @Override
    public void update(Long itemId, ItemUpdateDto updateParam) {
        Item findItem = repository.findById(itemId).orElseThrow();
        findItem.setItemName(updateParam.getItemName());
        findItem.setPrice(updateParam.getPrice());
        findItem.setQuantity(updateParam.getQuantity());
    }

    @Override
    public Optional<Item> findById(Long id) {
        return repository.findById(id);
    }

    @Override
    public List<Item> findAll(ItemSearchCond cond) {
        String itemName = cond.getItemName();
        Integer maxPrice = cond.getMaxPrice();

        if(StringUtils.hasText(itemName) && maxPrice != null) {
//            return repository.findByItemNameLikeAndPriceLessThanEqual("%" + itemName + "%", maxPrice);
            return repository.findItems("%" + itemName + "%", maxPrice);
        } else if (StringUtils.hasText(itemName)) {
            return repository.findByItemNameLike("%" + itemName + "%");
        } else if (maxPrice != null) {
            return repository.findByPriceLessThanEqual(maxPrice);
        } else {
            return repository.findAll();
        }
    }
}


ItemRepository를 상속받게하고, 생성자를 통해 앞에서 만든 SpringJpaDataRepository를 주입시킨다. 그리고 각 함수를 구현한다. JPA를 이용한 것보다 메소드 이름을 그대로 사용해서 훨씬 편리하게 사용할 수 있다.

728x90