Spring DB - 스프링 데이터 JPA

Kwon Yongho·2023년 5월 23일
0

Spring-DB

목록 보기
12/16
post-thumbnail

스프링 데이터 JPA

  1. 스프링 데이터 JPA 주요 기능
  2. 스프링 데이터 JPA 적용1
  3. 스프링 데이터 JPA 적용2

1. 스프링 데이터 JPA 주요 기능

스프링 데이터 JPA는 JPA를 편리하게 사용할 수 있도록 도와주는 라이브러리이다.

대표 기능

  • 공통 인터페이스 기능
  • 쿼리 메서드 기능

공통 인터페이스 기능

  • JpaRepository 인터페이스를 통해서 기본적인 CRUD 기능 제공한다.
  • 공통화 가능한 기능이 거의 모두 포함되어 있다.

JpaRepository 사용법

public interface ItemRepository extends JpaRepository<Item, Long> {
}
  • JpaRepository인터페이스를 인터페이스 상속 받고, 제네릭에 관리할 <엔티티, 엔티티ID> 를 주면 된다.
  • 그러면 JpaRepository가 제공하는 기본 CRUD 기능을 모두 사용할 수 있다.


. . . 등등등

스프링 데이터 JPA가 구현 클래스를 대신 생성

  • JpaRepository 인터페이스만 상속받으면 스프링 데이터 JPA가 프록시 기술을 사용해서 구현 클래스를 만들어준다. 그리고 만든 구현 클래스의 인스턴스를 만들어서 스프링 빈으로 등록한다.
  • 개발자는 구현 클래스 없이 인터페이스만 만들면 기본 CRUD 기능을 사용할 수 있다.

쿼리 메서드 기능

스프링 데이터 JPA는 인터페이스에 메서드만 적어두면, 메서드 이름을 분석해서 쿼리를 자동으로 만들고 실행해주는 기능을 제공한다.

스프링 데이터 JPA

public interface MemberRepository extends JpaRepository<Member, Long> {
 List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}
  • 스프링 데이터 JPA는 메서드 이름을 분석해서 필요한 JPQL을 만들고 실행해준다. 물론 JPQL은 JPA가 SQL로 번역해서 실행한다.
  • 그냥 아무 이름이나 사용하는 것은 아니고 다음과 같은 규칙을 따라야 한다.

쿼리 메소드 필터 조건

JPQL 직접 사용도 가능

public interface SpringDataJpaItemRepository extends JpaRepository<Item, Long>
{
	//쿼리 메서드 기능
	List<Item> findByItemNameLike(String itemName);
	//쿼리 직접 실행
	@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);
}
  • 쿼리 메서드 기능 대신에 직접 JPQL을 사용하고 싶을 때는 @Query와 함께 JPQL을 작성하면 된다. 이때는 메서드 이름으로 실행하는 규칙은 무시된다.

2. 스프링 데이터 JPA 적용1

SpringDataJpaItemRepository

package hello.itemservice.repository.jpa;

import hello.itemservice.domain.Item;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface SpringDataJpaItemRepository extends JpaRepository<Item, Long>
{
    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 기능을 사용할 수 있다.

  • 직접 쿼리 실행 시 쿼리 메서드 기능을 사용하거나 @Query를 사용해서 직접 쿼리를 실행하면 된다.

  • JPA는 동적 쿼리에 약하다. JPQL 동적 쿼리는 Querydsql을 사용하는 것이 좋다.

  • findAll(): 모든 ITEM 조회

  • findByItemNameLike(): 이름 조건만 검색 조희

  • findByPriceLessThanEqual(): 가격 존건만 검색 조회

  • findByItemNameLikeAndPriceLessThanEqual(): 이름과 가격 조건을 검색 조회

메서드 이름으로 쿼리를 실행하는 기능은 다음과 같은 단점이 있다.
1. 조건이 많으면 메서드 이름이 너무 길어진다.
2. 조인 같은 복잡한 조건을 사용할 수 없다.

3. 스프링 데이터 JPA 적용2

JpaItemRepositoryV2

package hello.itemservice.repository.jpa;

import hello.itemservice.domain.Item;
import hello.itemservice.repository.ItemRepository;
import hello.itemservice.repository.ItemSearchCond;
import hello.itemservice.repository.ItemUpdateDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;

@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();
        }
    }
}

여기서는 JpaItemRepositoryV2ItemRepositorySpringDataJpaItemRepository사이를 맞추기 위한 어댑터 처럼 사용된다.

클래스 의존 관계

런타임 객체 의존 관계

중간에서 JpaItemRepository가 어댑터 역할을 해준 덕분에 ItemService가 사용하는 ItemRepository 인터페이스를 그대로 유지할 수 있고 클라이언트인 ItemService의 코드를 변경하지 않아도 되는 장점이 있다.

findAll()
데이터를 조건에 따라 4가지로 분류해서 검색한다.

  • 모든 데이터 조회
  • 이름 조회
  • 가격 조회
  • 이름 + 가격 조회

-> 조건이 2개만 되어도 이름이 너무 길어지는 단점이 있다. 따라서 스프링 데이터 JPA가 제공하는 메서드 이름으로 쿼리를 자동으로 만들어주는 기능과 @Query로 직접 쿼리를 작성하는 기능 중에 적절한 선택이 필요하다.

설정 후 테스트 실행

  • 하이버네이트 버그
    하이버네이트 5.6.6~5.6.7을 사용하면 Like문장을 사용 시 예외가 발생했다.
    build.gradle 에서
ext["hibernate.version"] = "5.6.5.Final"

를 추가해서 버전을 낮춰주었다.

애플리케이션을 실행


잘 동작하는 것을 확인 할 수 있다.

참고
김영한: 스프링 DB 2편 - 데이터 접근 활용 기술(인프런)
Github - https://github.com/b2b2004/Spring_DB

0개의 댓글