728x90
스프링 데이터 JPA - MemberRepository 생성
- Interface로 MemberRepository를 만들고 스프링 데이터 JPA를 상속받음
- findByUsername()을 따로 만들어주면 By~를 스프링 데이터 JPA가 인식해서 다음과 같은 쿼리를 자동으로 실행하게 해줌
- select * from member where username = ?
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByUsername(String username);
}
사용자 정의 리포지토리
사용자 정의 리포지토리 사용법
- 사용자 정의 인터페이스 작성 (MemberRepositoryCustom.class)
- 사용자 정의 인터페이스 구현 (MemberRepositoryImpl.class)
- 스프링 데이터 리포지토리에 사용자 정의 인터페이스 상속 (MemberRepsotory extends MemberRepositoryCustom)
스프링 데이터 페이징 활용1 - Querydsl 페이징 연동
스프링 데이터 페이징 활용
- 스프링 데이터의 Page, Pageable 활용
- 전체 카운트를 한번에 조회하는 단순한 방법
- 데이터 내용과 전체 카운트를 별도로 조회하는 방법
- 스프링 데이터의 Page, Pageable활용
- MemberRepositoryImpl.java에 단순 페이징 쿼리 조회하는 로직 추가
- fetchResults()를 사용하면 count쿼리까지 같이 실행이 된다.
@Override
public Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable) {
QueryResults<MemberTeamDto> results = queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
team.id.as("teamId"),
team.name.as("teamName")
))
.from(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetchResults();// count쿼리 함께날림
List<MemberTeamDto> content = results.getResults();
long total = results.getTotal();
return new PageImpl<>(content, pageable, total);
}
- TestCode
@Test
public void searchPageSimpleTest() {
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
em.persist(teamA);
// teamA로 했더니 (Commit시) transaction marked 익셉션이 터졌다.
// Transaction silently rolled back because it has been marked as rollback-only
em.persist(teamB);
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 20, teamA);
Member member3 = new Member("member3", 30, teamB);
Member member4 = new Member("member4", 40, teamB);
em.persist(member1);
em.persist(member2);
em.persist(member3);
em.persist(member4);
MemberSearchCondition condition = new MemberSearchCondition();
PageRequest pageRequest = PageRequest.of(0, 3);
// condition.setAgeGoe(35);
// condition.setAgeLoe(40);
// condition.setTeamName("teamB");
Page<MemberTeamDto> result = memberRepository.searchPageSimple(condition, pageRequest);
assertThat(result.getSize()).isEqualTo(3);
assertThat(result.getContent()).extracting("username").containsExactly("member1", "member2", "member3");
}
- 결과 (count쿼리가 함께 실행된다.)
/* select
count(member1)
from
Member member1
left join
member1.team as team */ select
count(member0_.member_id) as col_0_0_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.id
2024-02-18 08:05:19.520 TRACE 17128 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_0_0_] : [BIGINT]) - [4]
2024-02-18 08:05:19.530 DEBUG 17128 --- [ main] org.hibernate.SQL :
/* select
member1.id as memberId,
member1.username,
member1.age,
team.id as teamId,
team.name as teamName
from
Member member1
left join
member1.team as team */ select
member0_.member_id as col_0_0_,
member0_.username as col_1_0_,
member0_.age as col_2_0_,
team1_.id as col_3_0_,
team1_.name as col_4_0_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.id limit ?
- 데이터와 카운트 쿼리를 별도로 조회하는 방법
- count쿼리를 최적화 하고 싶을 때 분리할 수 있다. (실제 쿼리가 복잡한데 카운트 쿼리는 단순할 때)
- count쿼리를 분리할 땐 fetch()를 사용해 content를 List로 반환하고 카운트쿼리는 fetchCount()를 사용한다.
@Override
public Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable) {
List<MemberTeamDto> results = queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
team.id.as("teamId"),
team.name.as("teamName")
))
.from(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();// count쿼리 함께날림
long total = queryFactory
.selectFrom(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
.fetchCount();
List<MemberTeamDto> content = results;
return new PageImpl<>(content, pageable, total);
}
스프링데이터 페이징 활용2 - CountQuery 최적화
- 때에 따라 카운트 쿼리를 생략
- 페이지 시작이면서 컨텐츠 사이즈가 페이지 사이즈보다 작을 때
- 마지막 페이지 일때 (offset + 컨텐츠 사이즈를 더해서 전체 사이즈 구함)
- PageableExecutionUtils.*getPage*(content, pageable, () -> countQuery.fetchCount()); 을 사용하면 스프링 데이터 JPA가 조건이 만족할 때 사용하는 기능을 제공해준다.
@Override
public Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable) {
List<MemberTeamDto> results = queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
team.id.as("teamId"),
team.name.as("teamName")
))
.from(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();// count쿼리 함께날림
JPAQuery<Member> countQuery = queryFactory
.selectFrom(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
);
List<MemberTeamDto> content = results;
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchCount);
}
스프링 데이터 JPA가 제공하는 Querydsl 기능
스프링 데이터 페이징 활용3 - 컨트롤러 개발
- 페이징 처리하는 로직 확인하기 위한 API 경로를 MemberController에 추가
@GetMapping("/v2/members")
public Page<MemberTeamDto> searchMemberV2(MemberSearchCondition condition, Pageable pageable) {
return memberRepository.searchPageSimple(condition, pageable);
}
@GetMapping("/v3/members")
public Page<MemberTeamDto> searchMemberV3(MemberSearchCondition condition, Pageable pageable) {
return memberRepository.searchPageComplex(condition, pageable);
}
- http://localhost:8080/v3/members?page=0&size=200 를 날리면 PageableExecutionUtils 은 카운트쿼리를 날리지 않는다.
Source Path
https://github.com/jang314/querydsl
GitHub - jang314/querydsl
Contribute to jang314/querydsl development by creating an account on GitHub.
github.com
'강의 > 실전! Querydsl' 카테고리의 다른 글
Section7. 스프링 데이터 JPA가 제공하는 Querydsl기능 (0) | 2024.04.06 |
---|---|
Section5. 실무활용 - 순수JPA와 Querydsl (1) | 2024.04.06 |
Section4. 중급문법 (0) | 2024.04.06 |
Section3. 기본문법 (0) | 2024.04.06 |