코딩공작소
JPA 프로그래밍 학습 정리 (9) - 스프링 데이터 JPA(1) 본문
스프링 데이터 JPA
기존의 CRUD문제를 세련된 방법으로 해결한다. 구현 클래스 없이 인터페이스만으로 데이터 접근계층에 접근할 수 있는 개발방법이다.
public interface MemberRepository extends JpaRepository<Member, Long>{
Member findByUsername(String username);
}
public interface ItemRepository extends JpaRepository<Item, Long> {}
스프링 프레임워크와 JPA를 함께 사용한다면 스프링 데이터 JPA 사용을 적극 추천한다.
스프링 데이터 JPA 설정
<!-- 스프링 데이터 JPA 라이브러리 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- 환경설정 XML 설정 -->
<jpa:repositories base-package = "jpabook.jpashop.repository" />
@Configuration
@EnableJpaRepositories(basePackages = "jpabook.jpashop.repository")
public class AppConfig {}
스프링 설정에 JavaConfig를 사용해줘야 한다.
공통 인터페이스 기능
1
2
3
4
|
public interface JpaRepository<T, ID extends Serializable> extends
PagingAndSortingRepository<T, ID>{...}
public interface MemberRepository extends JpaRepository<Member, Long> {}
|
cs |
회원 엔티티와 회원 엔티티의 식별자 타입을 지정해준다.
JpaRepository 인터페이스의 주요 메소드
- save(S) : 새로운 엔티티는 저장, 이미 있는 엔티티는 수정
- delete(T) : 엔티티 하나를 삭제
- findOne(ID) : 엔티티 하나를 조회
- getOne(ID) : 엔티티를 프록시로 조회한다
- findAll() : 모든 엔티티를 조회한다
쿼리 메소드 기능
메소드 이름만으로 쿼리를 생성하는 기능이 있다.
- 메소드 이름으로 쿼리 생성
- 메소드 이름으로 JPA NamedQuery 호출
- @Query 어노테이션을 사용해서 리포지토리 인터페이스에 쿼리 직접 정의
메소드 이름으로 쿼리 생성
정해진 규칙에 의해 이름을 정하면 자동으로 쿼리가 생성된다.
https://docs.spring.io/spring-data/jpa/docs/current/api/index.html
JPA 공식 문서에서 해당 쿼리들을 확인할 수 있다.
JPA NamedQuery
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public interface MemberRepository extends JpaRepository<Member, Long>{
List<Member> findByUsername(@Param("username") String username);
}
//같은 방식
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username"
)
public class Member {...}
<named-query name = "Member.findByUsername">
<query>...</query>
// 쿼리 호출
Member.findByUsername
|
cs |
스프링 데이터 JPA에서 실행할 Named쿼리가 없으면 메소드 이름으로 쿼리 생성 전략을 사용한다.
@Query, 리포지토리 메소드에 쿼리 정의
실행 시점에 문법 오류를 발견할 수 있는 장점이 있다.
1
2
3
4
5
6
7
8
9
10
11
12
|
public interface MemberRepository extends JpaRepository<Member, Long>{
@Query("select m from Member m where m.username = ?1")
Member findByUsername(String username);
}
public interface MemberRepository extends JpaRepository<Member, Long>{
@Query(value = "SELECT * FROM MEMBER WHERE USERNAME = ?0",
nativeQuery = true)
Member findByUsername(String username);
}
|
cs |
직접 쿼리를 정의할수도 있고 nativeSQL도 nativeQuery 속성여부를 통해 사용할 수 있다.
📌 파라미터 바운딩은 위치기반 보다는 이름기반 바인딩을 사용하자
벌크성 수정 쿼리
기존에 executeUpdate() 함수를 통해 수행했던 쿼리를 스프링 데이터 JPA를 통해서 할 수 있다.
1
2
3
4
|
@Modifying
@Query("update Product p set p.price = p.price * 1.1
where p.stockAmount < :stockAmount")
int bulkPriceUp(@Param("stockAmount") String stockAmount);
|
cs |
벌크성 쿼리를 실행하고 나서 영속성 컨텍스트를 초기화하고 싶으면 @Modifying(clearAutomatically = true)처럼 옵션을 사용해주면 된다.
반환 타입
- List<Member> findByName(String name) : 한 건이상 컬렉션, 조회 결과가 없으면 빈 컬렉션을 반환
- Member findByEmail(String email) : 단건이면 반환타입지정, 조회 결과가 없으면 null을 반환
페이징과 정렬
- org.springframework.data.domain.Sort : 정렬 기능
- org.springframework.data.domain.Pageable : 페이징 기능
1
2
3
4
5
6
7
|
//count 쿼리 사용
Page<Member> findByName(String name, Pageable pageble);
//count 쿼리 사용 안함
List<Member> findByName(String name, Pageble pageable);
List<Member> findByName(String name, Sort sort);
|
cs |
- 검색 조건 : 이름이 김으로 시작하는 회원
- 정렬 조건 : 이름으로 내림차순
- 페이징 조건 : 첫 번째 페이지, 페이지당 보여줄 데이터는 10건
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public interface MemberRepository extends Repository<Member, Long> {
Page<Member> findByStartingWith(String name,
Pageable Pageable);
}
//페이징 조건과 정렬 조건 설정
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 |
Page인터페이스도 다양한 메소드를 제공하니 공식문서를 찾아보자
이로써 Pageable, Page를 통해 페이징 처리를 개발할 수 있다.
사용자 정의 리포지토리 구현
1
2
3
4
5
6
7
8
9
10
11
12
|
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 {}
|
cs |
WEB 확장
스프링 데이터가 제공하는 Web 확장 기능을 활성화하려면 @EnableSpringDataWebSupport 어노테이션을 사용해야 한다.
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
public class WebAppConfig {...}
도메인 클래스 컨버터 기능
도메인 클래스 컨버터는 HTTP 파라미터로 넘어온 엔티티의 아이디로 엔티티 객체를 찾아서 바인딩해준다.
1
2
3
4
5
6
7
8
9
10
11
12
|
@RequestMapping()
public String memberUpdateForm(@RequestParam("id") Long id, Model model){
Member member = memberRepository.findOne(id); // 회원을 찾는다.
...
}
@RequestMapping()
public String memberUpdateForm(@RequestParam("id") Member member, Model model){
model.addAttribute("member", member);
...
}
|
cs |
페이징과 정렬 기능
HandlerMethodArgumentResolver 를 제공한다.
- 페이징 기능 : PageableHandlerMethodArgumentResolver
- 정렬 기능 : SortHandlerMethodArgumentResolver
1
2
3
4
5
6
|
@RequestMapping()
public String list(Pageable pageble, Model model) {
Page<Member> page = memberService.findMembers(pageable);
model.addAtrribute("members", page.getContent());
return "members/memberList";
}
|
cs |
- page : 현재 페이지, 0부터 시작
- size : 한 페이지에 노출할 데이터 건수
- sort : 정렬 조건을 정의한다.
/members?page=0&size=name,desc&sort=address.city
접두사
사용해야 할 페이징 정보가 둘 이상이면 접두사 @Qualifier 어노테이션을 사용한다.
public String list(
@Qualifier("member") Pageable memberPageable,
@Qualifier("order") Pageable orderPageber, ...
/members?member_page=0&order_page=1
기본값
Pageable의 기본값은 page=0, size=20이다.
@PageableDefault 어노테이션을 통해 기본값을 변경할 수 있다.
@RequestMapping()
public String list(@PageableDefault(size = 12, sort = "name",
direction = Sort.Direction.DESC) pageable pageable) { ... }
스프링 데이터 JPA는 simpleJpaRepository 클래스가 구현한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID extends Serializable>
implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
@Transactional
public <S extends T> S save(S entity){
if(entityInformation.isNew(entity)){
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
}
|
cs |
https://taehoon9393.tistory.com/376
JPA 프로그래밍 학습 정리 (9) - 스프링 데이터 JPA(2)
JPA 샵에 적용해보자 환경설정 Repository Refactoring 명세 적용 기타 환경설정 1 2 3 4 5 6 7 8 9 10 org.springframework.data spring-data-jpa 1.8.0.RELEASE
taehoon9393.tistory.com
'어플리케이션개발 > JPA' 카테고리의 다른 글
JPA 프로그래밍 학습 정리 (10) - 웹 애플리케이션과 영속성 관리 (0) | 2024.02.28 |
---|---|
JPA 프로그래밍 학습 정리 (9) - 스프링 데이터 JPA(2) (0) | 2024.02.22 |
JPA 프로그래밍 학습 정리 (8) - 웹 어플리케이션 제작(3) (0) | 2024.02.17 |
JPA 프로그래밍 학습 정리 (8) - 웹 어플리케이션 제작(2) (0) | 2024.02.16 |
JPA 프로그래밍 학습 정리 (8) - 웹 어플리케이션 제작(1) (0) | 2024.02.14 |