FireDrago
[Spring Data JPA] 페이징, 벌크성 수정쿼리, 엔티티 그래프 본문
스프링 데이터 JPA 페이징과 정렬
/**
* 페이징 쿼리는 반환타입에 따라 효과가 달라진다.
*
* 총 페이지 없이, limit에 한개 추가로 가져옴, 모바일 더보기 환경등에서 사용
* Slice<Member> findByAge(int age, Pageable pageable);
*
* 그냥 페이징 기능 필요없고, 데이터만 받아오고 싶다.
* List<Member> findByAge(int age, Pageable pageable);
*/
public interface MemberRepository extends Repository<Member, Long> {
// 메서드 이름으로 쿼리 작성 기능 + 페이징 기능
Page<Member> findByAge(int age, Pageable pageable);
}
페이징 정렬과 파라미터에는
org.springframework.data.domain.Sort : 정렬 기능
org.springframework.data.domain.Pageable : 페이징 기능 (내부에 Sort 포함)
org.springframework.data.domain.Slice : 추가 count 쿼리 없이 다음 페이지만 확인 가능 (모바일 환경의 더보기 등)
org.springframework.data.domain.Page : 추가 count 쿼리 결과를 포함하는 페이징
반환타입에 따라 페이징 효과가 달라진다.
Page<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용
Slice<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안함
List<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안함
List<Member> findByUsername(String name, Sort sort);
페이징 기능은 스프링 MVC 기능과 함께 통합된 사용이 가능하다. 컨트롤러의 파라미터로도 사용가능하다.
ex ) localhost:8080/members?page=2
@RestController
@RequiredArgsConstructor
public class MemberController {
@GetMapping("/members")
// @PageableDefault 어노테이션으로 기본 사이즈와 정렬 방식 설정가능
public Page<MemberDto> list(@PageableDefault(size = 12, sort = "username",
direction = Sort.Direction.DESC) Pageable pageable) {
return memberRepository.findAll(pageable).map(MemberDto::new);
}
}
Page 반환타입은 count 쿼리가 함께 실행된다. countQuery 역시 최적화 해줄수 있다.
전체 count 쿼리는 매우 무겁기 때문에 최적화가 중요하다.
// Page 반환타입 사용시 count 쿼리가 함께 실행된다. count 쿼리 역시 설정가능하다.
@Query(value = "select m from Member m left join m.team t",
countQuery = "select count(m.username) from Member m")
Page<Member> findByAge(int age, Pageable pageable);
!! 주의 !!
Page를 API 만들때 사용한다면 반드시 DTO를 반환해야 한다.
Page<Member> page = memberRepository.findByAge(10, pageRequest);
Page<MemberDto> dtoPage = page.map(m -> new MemberDto());
벌크성 수정쿼리
================================ 순수 JPA ======================================
public int bulkAgePlus(int age) {
int resultCount = em.createQuery(
"update Member m set m.age = m.age + 1" +
"where m.age >= :age")
.setParameter("age", age)
.executeUpdate();
return resultCount;
}
============================== 스프링 데이터 JPA ===========================
// 벌크성 수정쿼리는 @Modifying 애노테이션을 반드시 사용해야한다. (변경감지 비활성화)
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
엔티티 하나의 값을 변경하는 것이 아니라, DB의 전체 데이터를 수정해야할 경우도 있다. 스프링 데이터 JPA는 이때에도
필요한 기능을 제공한다.
주의할 점은 벌크성 수정쿼리는 영속성 컨텍스트를 사용하지 않는다.
따라서 영속성 컨텍스트의 엔티티와 DB의 데이터가 불일치할 위험이 있다.
벌크성 수정,삭제 이후에는 반드시 영속성 컨텍스트를 초기화 해야한다. @Modifying(clearAutomatically = true)
1. 영속성 컨텍스트에 엔티티가 없는 상태에서 벌크 연산을 먼저 실행한다.
2. 부득이하게 영속성 컨텍스트에 엔티티가 있으면 벌크 연산 직후 영속성 컨텍스트를 초기화 한다
엔티티 그래프
// JPQL로 패치 조인 -> N+1 문제를 해결하기위해 쿼리 한방으로 조회
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
// 공통 메서드 오버라이드
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
//JPQL + 엔티티 그래프
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
//메서드 이름으로 사용, 쿼리에서 특히 편리하다.
@EntityGraph(attributePaths = {"team"})
List<Member> findEntityGraphByUsername(String username);
지연로딩을 사용하면, 한번에 엔티티를 조회하려면 JPQL을 직접사용하여 join fetch 문을 작성해야한다
스프링 데이터 JPA는 EntityGraph 기능을 사용하여 편리하게 페치조인 기능을 사용할 수 있다.
@EntityGraph 어노테이션은 LEFT OUTER JOIN 을 사용한다.
