티스토리 뷰
| 소개
--
spring framework에서 JPA를 편리하게 사용할 수 있도록 지원하는 프로젝트
- CRUD 처리를 위한 공통 인터페이스 제공
- repository 개발 시 인터페이스만 작성하면 실행 시점에 스프링 데이터 JPA가 구현 객체를 동적으로 생성해서 주입
- 데이터 접근 계층을 개발할 때 구현 클래스 없이 인터페이스만 작성해도 개발을 완료할 수 있도록 지원
- 공통 메소드는 스프링 데이터 JPA가 제공하는 org.springframework.date.jpa.repository.JpaRepository 인터페이스에
count, delete, deleteAll, deleteAll, deleteById, existsById, findById, save ..
c.스프링 데이터 JPA 적용
- 공통 메소드가 아닐 경우에도 스프링 데이터 JPA가 메소드 이름을 분석해서 JPQL을 실행
1 2 3 4 5 6 7 | public interface MemberRepository extends JpaRepository<Member, Long> { Member findByUsername(String username); // select m from Member m where username = :username } public interface ItemRepository extends JpaRepository<Item, Long> { } | cs |
| 설정
--
> Library
+ spring-data-commons에 의존
1 2 3 4 5 6 7 | <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>2.4.2</version> </dependency> | cs |
> 환경설정
src\main\resources\appConfig.xml
- 스프링 설정 XML 파일에 <jpa:repositorise>를 사용
- repository를 검색할 base-package도 기입 (해당 패키지와 그 하위 패키지를 검색)
- basePackage에 있는 repository interface들을 찾아서
해당 interface를 구현한 클래스를 동적으로 생성한 후 스프링 빈으로 등록
1 | <jpa:repositories base-package="jpabook.jpashop.repository" /> | cs |
- JavaConfig를 사용할 경우
1 2 3 4 5 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @Configuration @EnableJpaRepositories(basePackages = "jpabook.jpashop.repository") public class AppConfig {} | cs |
| 공통 인터페이스 기능
--
1 2 3 4 5 6 7 8 9 | public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> { // ... } public interface MemberRepository extends JpaRepository<Member, Long> { // 제네릭에 회원 엔티티와 식별자 타입을 지정 Member findByUsername(String username); // select m from Member m where username = :username } | cs |
[ 스프링 데이터 모듈 ]
Repository
|
CrudRepository
|
PagingAndSortingRepository
|
[ 스프링 데이터 JPA 모듈 ]
|
JpaRepository (+ 추가로 JPA에 특화된 기능 제공)
ㅇ JpaRepository interface를 상속 받으면 사용할 수 있는 주요 메서드
- save(S) : 새로운 엔티티는 저장하고 이미 잇는 엔티티는 수정
(식별자 값이 없으면 em.persist(), 있으면 em.merge() 호출)
- delete(T) : 엔티티 하나를 삭제 (내부에서 em.remove() 호출)
- findOne(ID) : 엔티티 하나를 조회 (내부에서 em.find() 호출)
- getOne(ID) : 엔티티를 프록시로 조회 (내부에서 em.getReference() 호출)
- findAll(..) : 모든 엔티티를 조회 (sort 또는 pageable 조건을 파라미터로 제공)
| 쿼리 메소드 기능
--
메서드 이름만으로 쿼리 생성
인터페이스에 메소드만 선언하면 해당 메소드의 이름으로 적절한 JPQL 쿼리를 생성해서 실행
ㅇ JPA가 제공하는 쿼리 메소드 기능
> 메소드 이름으로 쿼리 생성
> JPA NamedQuery 호출
> @Query 어노테이션을 사용해서 리포지토리 인터페이스에 쿼리 직접 정의
||| 메소드 이름으로 쿼리 생성
--
Spring Data JPA - Reference Documentation
ㅇ 스프링 데이터 JPA 쿼리 생성 기능
- 엔티티의 필드명이 변경되면 인터페이스에 정의한 메소드 이름도 꼭 함께 변경해주어야 함
c.example
1 2 3 4 5 6 | // 이메일과 이름으로 회원 조회 public interface MemberRepository extends JpaRepository<Member, Long> { List<Member> findByEmailAndName(String email, string name); // select m from Member m where m.email - ?1 and m.name ?2 } | cs |
||| JPA NamedQuery 호출
--
메소드 이름으로 JPA Named 쿼리 호출
- 쿼리에 이름을 부여해서 사용
- Annotation이나 XML에 쿼리를 정의
c. @NamedQuery Annotaion으로 Named Query 정의
1 2 3 4 5 6 7 | @Entity @NamedQuery( name = "Member.findByUsername", query = "select m from Member m where m.username = :username") public class Member { ... } | cs |
c. orm.xml의 XML 사용
1 2 3 4 5 6 7 | <named-query name="Member.findByUsername"> <query><CDATA[ select m from Member m where m.username = :username ]></query> </named-query> | cs |
c. JPA를 직접 사용해서 Named 쿼리 호출
1 2 3 4 5 6 7 8 9 10 | public class MemberRepository { public List<Member> findByUsername(String username) { ... List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class) .setParameter("username", "회원1") .getResultList(); } } | cs |
c. 스프링 데이터 JPA로 Named 쿼리 호출
- 선언한 "도메인 클래스 + . + 메소드 이름" 으로 Named 쿼리를 찾아서 실행
- Member.findByUsername Named Query
- 만약, 실행한 Named Query가 없다면 메소드 이름으로 쿼리 생성 전략 사용
1 2 3 4 | public interface MemberRepository extends JpaRepository<Member, Long> { // Member Domain Class List<Member> findByUsername(@Param("username") String username); } | cs |
||| @Query Annotaion을 사용해서 Repository interface에 쿼리 직접 정의
--
ㅇ Repository 메소드에 직접 쿼리를 정의
- @org.springframework.date.jpa.repository.Query Annotation 사용
- 실행할 메소드에 정적 쿼리를 직접 작성 (이름 없는 Named 쿼리)
- 애플리케이션 실행 시점에 문법 오류를 발견할 수 있는 장점
1 2 3 4 5 | public interface MemberRepository extends JpaRepository<Member, Long> { @Query("select m from Member m where m.username = ?1") Member findByUsername(String username); } | cs |
ㅇ Native SQL을 사용할 경우
- @Query Annotation에 nativeQuery=true 설정
- 파라미터 바인딩이 0부터 시작
1 2 3 4 5 6 | public interface MemberRepository extends JpaRepository<Member, Long> { @Query(value = "select m from Member m where m.username = ?0", nativeQuery = true) Member findByUsername(String username); } | cs |
||| 파라미터 바인딩
--
> 위치 기반 파라미터 바인딩
1 | "select m from Member m where m.username = ?1" | cs |
> 이름 기반 파라미터 바인딩
1 | "select m from Member m where m.username = :name" | cs |
* 코드의 가독성과 유지보수를 위해 이름 기반 파라미터 바인딩을 권장
@org.springframework.date.repository.query.Param
1 2 3 4 5 | public interface MemberRepository extends JpaRepository<Member, Long> { @Query(value = "select m from Member m where m.username = :name") Member findByUsername(@Param("name") String username); } | cs |
||| 벌크성 수정 쿼리
ㅇ 스프링 데이터 JPA를 사용한 벌크성 수정 쿼리
@org.springframework.date.jpa.repository.Modifying
- 벌크성 쿼리를 실행한 후 영속성 컨텍스트를 초기화하고 싶다면 @Modifying(clearAutomatically=true)
1 2 3 4 5 | @Modifying @Query("update Product p set p.price = p.price * 1.1 where p.stockAmount < :stockAmount") int bulkPriceUp(@Param("stockAmount") string stockAmount); | cs |
||| 반환 타입
결과가 한 건 이상이면 컬렉션 인터페이스 사용
List<Member> findByName(String name);
- 조회 결과가 없다면 빈 컬렉션 반환
단건이면 반환 타입을 지정
Member findByEmail(String email);
- 조회 결과가 없다면 null 반환
- 결과가 2건 이상 조회되면 ..NonUniqueReulstException 예외 발생 (.getSingleResult() 메소드를 호출하므로)
||| 페이징과 정렬
--
스프링 데이터 JPA는 쿼리 메소드에 페이징과 정렬 기능 제공
> org.springframework.data.domain.Sort : 정렬 기능
> org.springframework.data.domain.Pageable : 페이징 기능(내부에 Sort 포함)
ㅇ 파라미터에 Pageable을 사용하면
- 반환 타입으로 List나 org.springframework.data.domain.Pageable 사용 가능
- 반환 타입으로 Page를 사용하면 스프링 데이터 JPA는 페이징 기능을 제공하기 위해
검색된 전체 데이터 건수를 조회하는 count쿼리를 추가로 호출
1 2 3 4 5 6 7 | // count 쿼리 사용 Page<Member> findByName(String name, Pageable Pageable); // count 쿼리 사용 X List<Mamber> findByName(String name, Pageable Pageable); List<Mamber> findByName(String name, Sort sort); | cs |
ㅇ 페이징과 정렬을 사용하는 예제
검색 조건 : 이름이 김으로 시작하는 회원
정렬 조건 : 이름으로 내림차순
페이징 조건 : 첫 번째 페이지, 페이지당 보여줄 데이터는 10건
c. Page 정의
1 2 3 4 | public interface MemberRepository extends Repository<Member, Long> { Page<Member> findByNameStartingWith(String name, Pageable Pageable); } | cs |
c. Page 실행
1 2 3 4 5 6 7 8 9 10 | // 페이징 조건과 정렬 조건 설정 PageRequest pageRequest = new PageRequest(0, 10, new Sort(Direction.DESC, "name")); Page<Member> result = memberRepository.findByNameStartingWith("김", pageRequest); List<Member> members = result.getContent(); // 조회된 데이터 int totalPages = result.getTotalPages(); // 전체 페이지 수 boolean hasNextPage = result.hasNextPage(); // 다음 페이지 존재 여부 | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public interface Page<T> extends Iterable<T> { int getNumber(); // 현재 페이지 int getSize(); // 페이지 크기 int getTotalPages(); // 전체 페이지 수 int getNumberOfElements(); // 현재 페이지에 나올 데이터 수 long getTotalElements(); // 전체 데이터 수 boolean hasPreviousPage(); // 이전 페이지 여부 boolean isFirstPage(); // 현재 페이지가 첫 페이지 인지 여부 boolean hasNextPage(); // 다음 페이지 여부 boolean isLastPage(); // 현재 페이지가 마지막 페이지 인지 여부 Pageable nextPageable(); // 다음 페이지 객체, 없으면 null Pageable previousPageable();// 이전 페이지 객체, 없으면 null List<T> getContent(); // 조회된 데이터 boolean hasContent(); // 조회된 데이터 존재 여부 Sort getSort(); // 정렬 정보 } | cs |
Next Post..
[Spring + JPA] Spring Data JPA 란? (2)
출처 : 자바 ORM 표준 JPA 프로그래밍
'Books' 카테고리의 다른 글
[Spring Data JPA] Make Web Application (3) (0) | 2020.12.28 |
---|---|
[Spring + JPA] Spring Data JPA 란? (2) (0) | 2020.12.28 |
[Spring + JPA] Make Web Application (2) (0) | 2020.12.24 |
[Spring + JPA] Make Web Application (1) (0) | 2020.12.23 |
[JPA] 벌크 연산이란? (3) | 2020.12.23 |