Spring Data JPA
JPA 기반으로 조회, 수정, 삭제, 삽입 등의 반복적인 패턴을 하는 상황이 생김
이를 좀 더 편하게 개발할 수 있도록 Spring에서 기반한 JPA가 나오게 됨
특징
1. 스프링 데이터 JPA는 스프링 프레임워크에서 JPA를 편리하게 사용할 수 있도록 지원하는 프로젝트
2. 데이터 접근 계층을 개발할 때 구현 클래스 없이 인터페이스만 작성해도 개발을 완료할 수 있음
쿼리 메서드
1. 일반적인 CRUD 메서드는 JpaRepository 인터페이스가 공통으로 제공하므로 문제가 없음.
2. 스프링 데이터 JPA는 메서드 이름을 분석해서 다음 JPQL을 실행함.
MemberRepository.findByUsername(...) -> 'select m from Member m where username =:username'
공통 인터페이스 기능
1. 스프링 데이터 JPA는 간단한 CRUD 기능을 공통으로 처리하는 JpaRepository 인터페이스를 제공.
2. 스프링 데이터 JPA를 사용하는 가장 단순한 방법은 이 인터페이스를 상속하는 것
3. 제네릭에 엔티티 클래스와 엔티티 클래스가 사용하는 식별자 타입을 지정하면 된다.
주요 메서드
save(S) : 새로운 엔티티는 저장하고 이미 있는 엔티티는 수정한다. 다시 말해서 새로운 id라면 삽입이 되고, 이미 있는 id가 존재한다면 수정이 되어진다.
delete(T) : 엔티티 하나를 삭제한다. 내부에서 EntityManager.remove() 를 호출한다.
findOne(ID) : 엔티티 하나를 조회한다. 내부에서 EntityManager.find() 를 호출한다. 즉시 조회를 하여 객체를 전달하는 방식이다.
getOne(ID) : 엔티티를 프록시로 조회한다. 내부에서 EntityManager.getReference() 를 호출한다. lazy-loading을 통해 객체를 전달하는 방식이다.
findAll(…): 모든 엔티티를 조회한다. 정렬( Sort )이나 페이징( Pageable ) 조건을 파라미터로 제공할 수 있다.
findOne vs getOne
findOne |
getOne |
공통점 |
|
id값에 해당하는 객체를 조회할 수 있다. |
|
차이점 |
|
엔티티를 조회할 때 연관된 엔티티 함께 조회 |
연관된 엔티티를 실제 사용할 때 조회 |
null 체크 가능 |
EntityNotFoundException 발생 |
- 메서드 이름으로 쿼리 생성
스프링 데이터 JPA
public interface MemberRepository extends Repository<Member, Long> {
List<Member> findByEmailAndName(String email, String name);
}
인터페이스에 정의한 findByEmailAndName(...) 메서드를 실행하면 스프링 데이터 JPA는 메서드 이름을 분석해서 JPQL을 생성하고 실행한다.
파라메터가 스트링이 아닌 하나의 객체로 받게 된다면?
밑의 그림과 같은 에러 메시지가 뜬다.
결론 : user 객체로 알아서 맵핑이 안되는 듯 하다.
@Query, 리파지토리 메서드에 쿼리 정의하기
1. @org.springframework.data.jpa.repository.Query 어노테이션을 사용함.
2. 이 방법은 실행할 메서드에 정적 쿼리를 직접 작성하므로 이름 없는 Named 쿼리라 할 수 있음
3. 또한 JPA Named 쿼리처럼 애플리케이션 실행 시점에 문법 오류를 발견할 수 있는 장점을 가짐
4. JPQL 뿐만이 아니라 상황에 따라서는 네이티브 쿼리를 사용할 수 있다.
1) JPQL 쿼리를 작성했을 때
- JPQL 쿼리를 사용할 때는 pageable 객체를 사용해 페이징을 쉽게 사용할 수 있음
2) 네이티브 쿼리를 작성했을 때
- "nativeQuery=true"를 체크하면 네이티브 쿼리를 사용할 수 있지만, JPQL처럼 pageable 객체 사용 불가능하다.
결국 이러한 에러메시지처럼 pageable 객체를 사용할 수 없고, 네이티브 쿼리에 페이징을 적용시켜야 한다.
Pagable 은 인터페이스다. 따라서 실제 사용할 때는 해당 인터페이스를 구현한 org.springframework.data.domain.PageRequest 객체를 사용한다. PageRequest 생성자의 첫 번째 파라미터에는 현재 페이지를, 두 번째 파라미터에는 조회할 데이터 수를 입력한다. 여기에 추가로 정렬 정보도 파라미터로 사용할 수 있다. 참고로 페이지는 0부터 시작한다.
====== Page 인터페이스 ======
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(); //정렬 정보
}
- 파라미터 바인딩
파라미터 바인딩을 왜 사용할까?
1. SQL Injection 걱정할 필요 없다.
2. 타입 바인딩을 알아서 해준다.
3. 하이버네이트 내부에서 최적화를 해주니 좀 더 성능상으로도 좋다.
기본은 위치기반이지만 위치기반(=?1),이름기반(=:name) 바인딩 모두 지원한다. 유지보수의 편리를 위해 이름기반으로 하는 것이 좋음
select m from Member m where m.username = ?1 //위치 기반
select m from Member m where m.username = :name //이름 기반
이름 기반 파라미터 바인딩을 사용하려면 spring에 있는 @Param 어노테이션을 사용하면 된다.
@Query(value = "select a from Account a where a.name = :name")
List<Account> findByusernamesNamedQueryNative(@Param("name") String name);
- 벌크성 수정 쿼리
벌크성 수정이란? 대량의 데이터들을 쿼리를 통해 수정한다는 의미. 대량 처리를 했을 시에는 속도가 빠르지만 한 행 처리에 있어서는 다소 느린 점이 있음.
원래 executeUpdate() 를 쓰던구문을 spring jpa에서는 @Modifying 어노테이션을 쓰면된다
@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);
- 반환타입
메서드이름으로 호출할때 메서드의 타입으로 반환타입을 설정해주면 된다.
List<Member> findByname(String name); //컬렉션
Member findByEmail(String email);//단건
만약 조회 결과가 없으면 컬렉션은 빈 컬렉션을 반환하고 단 건은 null 을 반환한다.
그리고 단건을 기대하고 반환 타입을 지정했는데 결과가 2건 이상 조회되면
NonUniqueResultException 예외가 발생한다.
참고로 단건으로 지정한 메서드를 호출하면 스프링 데이터 JPA는 내부에서 JPQL의
Query.getSingleResult() 메서드를 호출한다. 이 메서드를 호출했을 때 조회 결과가 없으면
javax.persistence.NoResultException 예외가 발생하는데 개발자 입장에서 다루기가 상당히 불편하다. 스프링 데이터 JPA는 단건을 조회할 때 이 예외가 발생하면 예외를 무시하고 대신에 null을 반환한다.
'JPA' 카테고리의 다른 글
JPA(Java Persistence API) 기본 개념 및 장단점 (0) | 2020.02.12 |
---|