ํด๋ฆญ ์ ์ด๋ํฉ๋๋ค.
์์ JPA ์์์ ํ์ด์ง
๋ค์ ๋ ๋ฉ์๋๋ฅผ ํตํด ํ์ด์ง์ ์งํํฉ๋๋ค
- setFirstResult(int startPosition) : ์กฐํ ์์ ์์น, 0๋ถํฐ ์์ํฉ๋๋ค.
- setMaxResult(int maxResult) : ์กฐํํ ๋ฐ์ดํฐ ์
setFirstResult๋ ํ์ด์ง์ ๋ฒํธ๊ฐ ์๋ ์กฐํ๋ฅผ ์์ํ row์ ์์น๋ฅผ ์ค์ ํ๋ ๊ฒ์ ๋๋ค.
์์ ์ฝ๋
em.createQuery("select m from Member m where m.age = :age order by m.username desc", Member.class)
.setParameter("age", age)
.setFirstResult(0) //์์ ์์น ์ง์
.setMaxResult(10) //์กฐํํ ๋ฐ์ดํฐ ๊ฐ์ ์ง์
.getResultList();
ํ์ด์ง๋ณ ์กฐํ ๋ฐฉ๋ฒ
N๋ฒ์งธ ํ์ด์ง์ ์์ ์์น : $(\;N\;-\;1\;) \; \times \;$ ํ์ด์ง ์ฌ์ด์ฆ
์ฒซ๋ฒ์งธ ํ์ด์ง๋ฅผ ์กฐํํ๊ณ ์ถ๋ค๋ฉด setFirstResult()์ 0์,
๋๋ฒ์งธ ํ์ด์ง๋ฅผ ์กฐํํ๊ณ ์ถ๋ค๋ฉด, [1 * ํ์ด์ง ์ฌ์ด์ฆ]๋ฅผ,
Nํ์ด์ง๋ฅผ ์กฐํํ๊ณ ์ถ๋ค๋ฉด [(N-1) * pageSize]๋ฅผ ์ค์ ํด ์ฃผ๋ฉด ๋ฉ๋๋ค.
Data JPA์์์ ํ์ด์ง
์ ๋ ฌ์ ์ํ ํ๋ผ๋ฏธํฐ
org.springframework.data.domain.Sort
์ ๋ ฌ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
ํ์ด์ง์ ์ํ ํ๋ผ๋ฏธํฐ
org.springframework.data.domain.Pageable
ํ์ด์ง ๊ธฐ๋ฅ๊ณผ ๋ด๋ถ์ Sort ํฌํํ๊ณ ์๊ธฐ์ ์ ๋ ฌ ๊ธฐ๋ฅ๋ ๊ฐ์ด ์ ๊ณตํฉ๋๋ค.
์ฆ Sort๋ ํ์ด์ง ๊ธฐ๋ฅ์ด ์๊ณ , Pageable์ Sort์ ํ์ด์ง ๊ธฐ๋ฅ๊น์ง ์ถ๊ฐ๋ ๊ฒ์ ๋๋ค.
๋ฐํ ํ์
- org.springframework.domain.Page
- ์ถ๊ฐ count ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
- ํ์ฌ ํ์ด์ง์ ๋ํ ์ ๋ณด์, ์ ์ฒด ๋ฐ์ดํฐ์ ๊ฐ์๋ฅผ ์กฐํํฉ๋๋ค.
- org.springframework.domain.Slice
- ์ถ๊ฐ count ์ฟผ๋ฆฌ ์์ด ๋ค์ ํ์ด์ง๋ง ํ์ธ ๊ฐ๋ฅํฉ๋๋ค.
- ๋ด๋ถ์ ์ผ๋ก limit +1์ ์กฐํํ์ฌ ๋ค์ ํ์ด์ง๊ฐ ์๋์ง ์๋์ง๋ง ํ์ธํฉ๋๋ค.
- List : ์ถ๊ฐ count ์ฟผ๋ฆฌ ์์ด ๊ฒฐ๊ณผ๋ง ๋ฐํํฉ๋๋ค.
์ฌ์ฉ ๋ฐฉ๋ฒ
Page<Member> findByAge(int age, Pageable pageable); //count ์ฟผ๋ฆฌ ์ฌ์ฉ
Page<Member> findByAge(int age, Sort sort); //count ์ฟผ๋ฆฌ ์ฌ์ฉ
Slice<Member> findByAge(int age, Pageable pageable); //count ์ฟผ๋ฆฌ ์ฌ์ฉ์ํจ
Slice<Member> findByAge(int age, Sort sort); //count ์ฟผ๋ฆฌ ์ฌ์ฉ์ํจ
List<Member> findByAge(int age, Pageable pageable); //count ์ฟผ๋ฆฌ ์ฌ์ฉ์ํจ
List<Member> findByAge(int age, Sort sort);
๋ฐ๋ก ์ฝ๋ ๊ตฌํ ์์ด, ์ธ์๋ฅผ ๋๊ฒจ์ฃผ๋ ๊ฒ๋ง์ผ๋ก๋ ํ์ด์ง ํน์ ์ ๋ ฌ์ด ๊ฐ๋ฅํฉ๋๋ค.
PageRequest - Pageable ๊ตฌํ์ฒด
PageRequest pageRequest = PageRequest.of(ํ์ด์ง, ํ์ด์งํ ์ฌ์ด์ฆ, Sort.by(Sort.Direction.DESC, "age"));
PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "age"));
0์ ํ์ด์ง, 3์ ํ์ด์งํ ์ฌ์ด์ฆ์ ๋๋ค.
Sort ์กฐ๊ฑด์ ์์ด๋ ๋ฉ๋๋ค.
ํ์ด์ง๋ 0๋ถํฐ ์์ํ๋ฉฐ, ์์ JPA์ setFirstResult์ ๋ฌ๋ฆฌ ํ์ด์ง ์๋ฅผ ์ง์ ๋ช ์ํด์ค๋๋ค.
์์
@Test
fun paging() {
//given
val member1 = Member(username = "member1", age = 1)
val member2 = Member(username = "member2", age = 1)
val member3 = Member(username = "member3", age = 1)
val member4 = Member(username = "member4", age = 1)
val member5 = Member(username = "member5", age = 1)
val member6 = Member(username = "member6", age = 1)
val member7 = Member(username = "member7", age = 1)
val member8 = Member(username = "member8", age = 1)
val member9 = Member(username = "member9", age = 1)
val member10 = Member(username = "member10", age = 1)
memberRepository.save(member1)
memberRepository.save(member2)
memberRepository.save(member3)
memberRepository.save(member4)
memberRepository.save(member5)
memberRepository.save(member6)
memberRepository.save(member7)
memberRepository.save(member8)
memberRepository.save(member9)
memberRepository.save(member10)
val pageRequest = PageRequest.of(1, 3, Sort.by(Sort.Direction.DESC, "username"))
//when
val findByUsername = memberRepository.findByAge(10, pageRequest)
//then
findByUsername.forEach{println(it)}
}
๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์นด์ดํธ ์ฟผ๋ฆฌ์ ๋ถ๋ฆฌ
์นด์ดํธ์ ๊ฐ์๋ฅผ ์์ด์ฌ ๋ left ์กฐ์ธ์ด ์คํ๋๋ ๊ฒฝ์ฐ์ ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ๋ ๊ฒฐ๊ณผ ๊ฐ์๊ฐ ๋ฌ๋ผ์ง์ง ์์ต๋๋ค.
์ฆ ์ฑ๋ฅ์ ์ ํ์ํค๋ join ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆด ํ์๊ฐ ์๋ ๊ฒ์ ๋๋ค.
๋ฐ๋ผ์ JPA๋ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํด ์นด์ดํธ ์ฟผ๋ฆฌ๋ฅผ ๋ถ๋ฆฌํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
public interface MemberRepository extends JpaRepository<Long, Member> {
@Query(
value = “select m from Member m left join m.team”,
countQuery = “select count(m.username) from Member m”)
Page<Member> findMemberAllCountBy(Pageable pageable);
}
์ฟผ๋ฆฌ DSL์์์ ํ์ด์ง
offset๊ณผ limit๋ฅผ ํตํด์ ํ์ด์ง์ ์ํํฉ๋๋ค.
offset์ 0๋ถํฐ ์์ํ๋ฉฐ, ๋ช ๋ฒ์งธ row์์ ๋ฐ์ดํฐ ์กฐํ๋ฅผ ์์ํ ์ง ์ ํ๋ค๊ณ ํ์์ต๋๋ค.
limit๋ ํ ํ๋ฉด์ ๋ณด์ฌ์ค ๋ฐ์ดํฐ์ ๊ฐ์์ ๋๋ค.
์กฐํ ๊ฑด์ ์ ํ
/**
* ํ์ด์ง ์ฒ๋ฆฌ
* offset - ํ์ด์ง ์๊ฐ ์๋๋ผ row ์
* limit - ๋ช๊ฐ ๊ฐ์ง๊ณ ์ฌ์ง
*/
@Test
fun testPaging() {
//given
val pageable = PageRequest.of(2, 3)
println(pageable.offset)//page * size
println(pageable.pageNumber)
val content = query
.selectFrom(member)
.orderBy(member.age.asc())
.offset(pageable.offset)
.limit(pageable.pageSize.toLong())
.fetch()
}
Data JPA + QueryDSL
Count ์ฟผ๋ฆฌ๋ฅผ ๋ถ๋ฆฌํ ํ์ด์ง ์ต์ ํ
/**
* ํ์ด์ง ์ฒ๋ฆฌ
* offset - ํ์ด์ง ์๊ฐ ์๋๋ผ row ์
* limit - ๋ช๊ฐ ๊ฐ์ง๊ณ ์ฌ์ง
*/
@Test
fun testPaging() {
//given
val pageable = PageRequest.of(2, 3)
println(pageable.offset)//page * size
println(pageable.pageNumber)
val content = query
.selectFrom(member)
.orderBy(member.age.asc())
.offset(pageable.offset)
.where(/** ์กฐ๊ฑด */)
.limit(pageable.pageSize.toLong())
.fetch()
val countQuery = query
//.select(Wildcard.count) -> select count(*)
.select(member.count())
.from(member)
.where(/** ์กฐ๊ฑด */)
val page: Page<Member> = PageableExecutionUtils.getPage(content, pageable) { countQuery.fetchOne()!! }
assertThat(page.size).isEqualTo(pageable.pageSize)//๋ช๊ฐ์ฉ ์๋ผ์ ํ์ด์ง ํ์๋๊ฐ
assertThat(page.totalPages).isEqualTo(4)//์ ์ฒด ํ์ด์ง ์
assertThat(page.totalElements).isEqualTo(12)//์ ์ฒด ์กด์ฌํ๋ ํ์ ์
assertThat(page.number).isEqualTo(pageable.pageNumber)
assertThat(page.numberOfElements).isEqualTo(3)//ํด๋น ํจ์ด์ง์ ์กด์ฌํ๋ ์์์ ๊ฐ์
assertThat(page.content[0].username).isEqualTo("member7")
}
Querydsl์ fetchResults() , fetchCount()๋ 5.0 ๋ฒ์ ์ดํ๋ถํฐ Deprecated ๋์์ต๋๋ค.
๋ฐ๋ผ์ count ์ฟผ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ ์์ ์์์ฒ๋ผ ์์ฑํ์ฌ์ผ ํฉ๋๋ค.
PageableExecutionUtils.getPage() ๋ฅผ ์ฌ์ฉํ์๋๋ฐ, ์ด๋ ์คํ๋ง Data ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๊ณตํฉ๋๋ค.
ํ์ด์ง์ ์์์ด๋ฉด์ ์ปจํ ์ธ ์ฌ์ด์ฆ๊ฐ ํ์ด์ง ์ฌ์ด์ฆ๋ณด๋ค ์๊ฑฐ๋,
๋ง์ง๋ง ํ์ด์ง์ธ ๊ฒฝ์ฐ์๋ count์ฟผ๋ฆฌ๋ฅผ ์๋ตํด์ ์ฒ๋ฆฌํฉ๋๋ค.
Spring Data JPA + QueryDSL + MVC Controller
์คํ๋ง ๋ฐ์ดํฐ JPA๋ฅผ ์ฌ์ฉํ๋ฉด, Controller๋ฅผ ํตํด Pagable ๊ฐ์ฒด๋ฅผ ๋ฐ๋ก ๋ฐ์ ์ ์์ผ๋ฉฐ, ๊ทธ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
?page=2&size=5
์์ ์์์์๋ ๋ค์๊ณผ ๊ฐ์ Pagable์ด ๋ฐํ๋๋ค.
- pageable.getOffset() -> 10
- pageable.getPageNumber() -> 2
- pageable.getPageSize=5;
Offset ๊ณ์ฐ
offset์ ํ์ด์ง์ ๋ฒํธ๊ฐ ์๋๋ผ, ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ธฐ ์์ํ row์ ์์น์ ๋๋ค.(0๋ถํฐ ์์)
๋ฐ๋ผ์ ํ์ด์ง์ ์ฌ์ด์ฆ๊ฐ 5์ด๊ณ , 0๋ฒ์งธ ํ์ด์ง๋ฅผ ์กฐํํ๊ณ ์ถ๋ค๋ฉด offset์ 0,
1๋ฒ์งธ ํ์ด์ง๋ฅผ ์กฐํํ๊ณ ์ถ๋ค๋ฉด 5,
2๋ฒ์งธ ํ์ด์ง๋ฅผ ์กฐํํ๊ณ ์ถ๋ค๋ฉด 10,
์ด๋ฐ ์์ผ๋ก ๋ฐํ๋์ด์ผ ํ๋ ๊ฒ์ ๋๋ค.
์ค์ ๋ก Pageable์ ๊ตฌํํ ์ถ์ ํด๋์ค์ธ AbstractPageRequest๋ฅผ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
Reference
[์๋ฐ ORM ํ์ค JPA ํ๋ก๊ทธ๋๋ฐ - ๊น์ํ]
'๐๏ธ Spring > JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring Data JPA] @EntityGraph (0) | 2022.07.28 |
---|---|
[Spring Data JPA] ๋ฒํฌ ์ฐ์ฐ (0) | 2022.07.28 |
[Spring Data JPA] - ๋ฐํ ํ์ ์ ์ข ๋ฅ (0) | 2022.07.28 |
[Spring Data JPA] - ์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ (0) | 2022.07.28 |
[Spring Data JPA] - ์ค์ ๊ฐ๋ ์๊ฐ (0) | 2022.07.28 |