본문 바로가기

JPA

[개념] JPA 프로그래밍 - 12. Spring Data JPA

스프링 데이터 JPA 소개

  • 스프링 데이터 JPA는 스프링 프레임워크에서 JPA를 편리하게 사용할 수 있도록 지원하는 프로젝트
  • CRUD 처리를 위한 공통 인터페이스를 제공 -> 데이터 접근 계층을 개발할 때 구현 클래스 없이 인터페이스만 작성해도 개발을 완료할 수 있음
  • save(), findOne(), findAll() 등등 일반적은 CRUD메소드는 JpaRepository 인터페이스가 공통으로 제공하므로 개발자가 구현하지 않아도 된다.
  • 스프링 프레임워크와 JPA를 함께 사용한다면 스프링 데이터 JPA 사용을 적극 추천
public interface MemberRepository extends JpaRepository<Member, Long> {
}

공통 인터페이스 기능

  • 스프링 데이터 JPA는 간단한 CRUD 기능을 공통으로 처리하는 JpaRepository 인터페이스를 제공한다.
  • JpaRepository 인터페이스 계층 구조
    • 윗부분에 스프링 데이터 모듈이 있고 그 안에 Repository, CrudRepository, PagingAndSortingRepository가 있는데 이것은 스프링 데이터 프로젝트가 공통으로 사용하는 인터페이스이다.
      JpaRepository 인터페이스는 여기에 추가로 JPA에 특화된 기능을 제공한다.
    • save(S), delete(T), findOne(ID), getOne(ID), findAll(...) ... 등 JpaRepository 인터페이스를 상속받으면 사용할 수 있는 주요 메소드들이 있다.

쿼리 메소드 기능

1. 메소드 이름으로 쿼리 생성

  • 메소드 이름만으로 쿼리를 생성해준다.
  • 정해진 규칙
Keyword Sample JPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… where x.firstname = 1?

Between

findByStartDateBetween

… where x.startDate between 1? and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age ⇐ ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1 (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1 (parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1 (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> age)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

 

2. JPA NamedQuery

  • 어노테이션으로 쿼리 정의
@Entity
@NamedQuery(
  name="Member.findByUsername",
  query="select m from Member m where m.username=:username")
public class Member {
 
}

 

  • XML에 쿼리 정의
<named-query name="Member.findByUsername">
  <query><CDATA[
    select m
    from Member m
    where m.username=:username
  ]></query>
</named-query>

 

public interface MemberRepository extends JpaRepository<Member, Long> {
  List<Member> findByUsername(@Param("username") String username);  
  // 메소드 이름만으로 Named쿼리 호출, 만약 Named쿼리가 없다면 메소드 이름으로 쿼리 생성 전략 사용
}

 

3. @Query, 리포지토리 메소드에 쿼리 정의

  • 실행할 메소드에 정적 쿼리를 직접 작성하므로 이름 없는 Named 쿼리라 할 수 있다.
  • JPA Named 쿼리처럼 애플리케이션 실행 시점에 문법 오류를 발견할 수 있는 장점이 있다.
  • 메소드에 JPQL 쿼리 작성
public interface MemberRepository extends JpaRepository<Member, Long> {
  @Query("select m from Member m where m.username=?1")  //위치기반 파라미터 바인딩
  Member findByUsername(String username);
}

 

  • JPA 네이티브 SQL 지원
public interface MemberRepository extends JpaRepository<Member, Long> {
  @Query(value="select * from Member where username=:name", nativeQuery=true)  //이름기반 파라미터 바인딩
  Member findByUsername(@Param("name") String username);
}

 

4. 페이징과 정렬

  • 페이징과 정렬 사용 예제
//count 쿼리 사용
Page<Member> findByName(String name, Pageable pageable);
 
//count 쿼리 사용 안함
List<Member> findByName(String name, Pageable pageable);
 
List<Member> findByName(String name, Sort sort);

 

  • Page 사용 예제 실행 코드
List<Member> members = result.getContent();  //조회된 데이터
int totalPages = result.getTotalPages();  //전체 페이지 수
boolean hasNextPage = result.hasNextPage();  //다음 페이지 존재 여부

사용자 정의 리포지토리 구현

public interface MemberRepositoryCustom {
  public List<Member> findMemberCustom();
}
 
public class MemberRepositoryImpl implements MemberRepositoryCustom {
  @Override
  public List<Member> findMemberCustom() {
    //사용자 정의 구현
  }
}        
  
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
}

스프링 데이터 JPA와 QueryDSL 통합

  1. QueryDslPredicateExecutor 사용
    • 단점 : join, fetch 사용 불가
  2. QueryDslRepositorySupport 사용
public class OrderRepositoryImpl extends QueryDslRepositorySupport implements CustomOrderRepository {
  public OrderRepositoryImpl() {
    super(Order.class);
  }
 
  @Override
  public List<Order> search(OrderSearch orderSearch) {
    QOrder order = QOrder.order;
    QMember member = QMember.member;
 
    JPQLQuery query = from(order);
 
    if (StringUtils.hasText(orderSearch.getMemberName())) {
      query.leftJoin(order.member, member)
          .where(member.name.contains(orderSearch.getMemberName()));
    }
 
    if (orderSearch.getOrderStatus() != null) {
      query.where(order.status.eq(orderSearch.getOrderStatus()));
    }
 
    return query.list(order);
  }
}

 

 

출처도서 : 자바 ORM 표준 JPA 프로그래밍 - 김영한 지음