이번 섹션에서는 [데이터 접근 기술 - 스프링 데이터 JPA]에 대해서 알아보자.
👉 목차는 다음과 같다.
1) 스프링 데이터 JPA 주요 기능
2) 스프링 데이터 JPA 적용1
3) 스프링 데이터 JPA 적용2
4) 정리
바로 하나씩 알아보자.
스프링 데이터 JPA는 JPA를 편리하게 사용할 수 있도록 도와주는 라이브러리이다.
수많은 편리한 기능을 제공하지만 가장 대표적인 기능은 다음과 같다.
공통 인터페이스 기능
JpaRepository
인터페이스를 통해서 기본적인 CRUD 기능을 제공한다.CrudRepository
에서 fineOne()
-> findById()
로 변경되었다.JpaRepository
인터페이스를 인터페이스 상속 받고, 제네릭에 관리할 <엔티티, 엔티티ID>
를 주면 된다.JpaRepository
가 제공하는 기본 CRUD 기능을 모두 사용할 수 있다.JpaRepository
인터페이스만 상속받으면 스프링 데이터 JPA가 프록시 기술을 사용해서 구현 클래스를 만들어준다. 그리고 만든 구현 클래스의 인스턴스를 만들어서 스프링 빈으로 등록한다.
쿼리 메서드 기능
스프링 데이터 JPA는 인터페이스에 메서드만 적어두면, 메서드 이름을 분석해서 쿼리를 자동으로 만들고 실행해주는 기능을 제공한다.
find…By
, read…By
, query…By
, get…By
count…By
반환타입 long
exists…By
반환타입 boolean
delete…By
, remove…By
반환타입 long
findDistinct
, findMemberDistinctBy
findFirst3
, findFirst
, findTop
, findTop3
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.limit-query-result
@Query
와 함께 JPQL을 작성하면 된다. 이때는 메서드 이름으로 실행하는 규칙은 무시된다.
✔️ 중요
스프링 데이터 JPA를 프로젝트에 적용해보자.
설정
스프링 데이터 JPA는 spring-boot-starter-data-jpa
라이브러리를 넣어주면 된다.
build.gradle
추가spring-boot-starter-data-jpa
라이브러리를 넣어주었다. 여기에는 JPA
, 하이버네이트, 스프링 데이터 JPA( spring-data-jpa
), 그리고 스프링 JDBC 관련 기능도 모두 포함되어 있다.
스프링 데이터 JPA 적용
JpaRepository
인터페이스를 인터페이스 상속 받으면 기본적인 CRUD 기능을 사용할 수 있다.@Query
를 사용해서 직접 쿼리를 실행하면 된다.Example
이라는 기능으로 약간의 동적 쿼리를 지원하지만, 실무에서 사용하기는 기능이 빈약하다. 실무에서 JPQL 동적 쿼리는 Querydsl을 사용하는 것이 좋다.
✔️ 코드 설명
JpaRepository
공통 인터페이스가 제공하는 기능이다.Item
을 조회한다.select i from Item i
select i from Item i where i.name like ?
select i from Item i where i.price <= ?
select i from Item i where i.itemName like ? and i.price <= ?
@Query
애노테이션을 사용하면 된다.@Param("itemName")
애노테이션을 사용하고, 애노테이션의 값에 파라미터 이름을 주면 된다.
의존관계와 구조
ItemService
는 ItemRepository
에 의존하기 때문에 ItemService
에서 SpringDataJpaItemRepository
를 그대로 사용할 수 없다.
물론 ItemService
가 SpringDataJpaItemRepository
를 직접 사용하도록 코드를 고치면 되겠지만, 우리는 ItemService
코드의 변경없이 ItemService
가 ItemRepository
에 대한 의존을 유지하면서 DI를 통해 구현 기술을 변경하고 싶다.
따라서 조금 복잡하지만, 새로운 리포지토리를 만들어서 이 문제를 해결해보자. 여기서는 JpaItemRepositoryV2
가 ItemRepository
와 SpringDataJpaItemRepository
사이를 맞추기 위한 어댑터 처럼 사용된다.
클래스 의존 관계
JpaItemRepositoryV2
는 ItemRepository
를 구현한다. 그리고 SpringDataJpaItemRepository
를 사용한다.런타임 객체 의존 관계
itemService
-> jpaItemRepositoryV2
-> springDataJpaItemRepository(프록시 객체)
이렇게 중간에서 JpaItemRepositoryV2
가 어댑터 역할을 해준 덕분에 ItemService
가 사용하는 ItemRepository
인터페이스를 그대로 유지할 수 있고 클라이언트인 ItemService
의 코드를 변경하지 않아도 되는 장점이 있다.
👉 다음으로 기능에 대해서 알아보자.
repository.save(item)
save()
를 호출한다. findById()
메서드를 사용해서 엔티티를 찾는다.repository.findById(itemId)
findById()
메서드를 사용해서 엔티티를 찾는다.findByItemNameLikeAndPriceLessThanEqual()
를 사용해도 되고, repository.findItems()
를 사용해도 된다. 그런데 보는 것 처럼 조건이 2개만 되어도 이름이 너무 길어지는 단점이 있다. 따라서 스프링 데이터 JPA가 제공하는 메서드 이름으로 쿼리를 자동으로 만들어주는 기능과 @Query
로 직접 쿼리를 작성하는 기능 중에 적절한 선택이 필요하다.
👉 기능을 모두 개발했으니, 설정하고 실행해보자.
SpringDataJpaItemRepository
는 스프링 데이터 JPA가 프록시 기술로 만들어주고 스프링 빈으로도 등록해준다.SpringDataJpaConfig
를 사용하도록 변경했다.ItemRepositoryTest
를 통해서 리포지토리가 정상 동작하는지 확인해보자.findItems
에서 오류가 발생한다. 아래 주의! - 하이버네이트 버그를 참고하자.ItemServiceApplication
를 실행해서 애플리케이션이 정상 동작하는지 확인해보자.@Repository
와 관계없이 예외가 변환된다.
✔️ 주의! - 하이버네이트 버그
5.6.6 ~ 5.6.7
을 사용하면 Like
문장을 사용할 때 다음 예외가 발생한다.java.lang.IllegalArgumentException: Parameter value [\] did not match expected type [java.lang.String (n/a)]
2.6.5
버전은 문제가 되는 하이버네이트 5.6.7
을 사용한다.build.gradle
에 다음을 추가해서 하이버네이트 버전을 문제가 없는 5.6.5.Final
로 맞추자.ext["hibernate.version"] = "5.6.5.Final"
강의를 듣고 정리한 글입니다. 코드와 그림 등의 출처는 김영한 강사님께 있습니다.