티스토리 뷰

반응형


| 소개


--

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(010new Sort(Direction.DESC, "name"));
 
Page<Member> result = 
    memberRepository.findByNameStartingWith("김", pageRequest);
 
List<Member> members = result.getContent();     // 조회된 데이터
int totalPages = result.getTotalPages();        // 전체 페이지 수
boolean hasNextPage = result.hasNextPage();     // 다음 페이지 존재 여부
cs


Page interface에서 제공하는 메소드

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 프로그래밍

반응형
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday