JPA

Spring Data JPA

개발만파볼까 2018. 4. 30. 21:24
728x90
반응형
SMALL

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을 반환한다.

 


728x90
반응형
LIST