Spring๐Ÿƒ

SpringData JPA ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ

wannaDevelopIt 2023. 2. 3. 17:37

์ถœ์ฒ˜ : ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„

 

in PagingAndSortingRepository

ํŽ˜์ด์ง• ํ”„๋กœ์„ธ์Šค

  1. PageRequest ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Pageable์— ํŽ˜์ด์ง• ์ •๋ณด๋ฅผ ๋‹ด์•„ ๊ฐ์ฒดํ™”
  2. Pageable์„ JpaRepository๊ฐ€ ์ƒ์†๋œ ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฉ”์„œ๋“œ์— T(Entity)์™€ ํ•จ๊ผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ
  3. return ์œผ๋กœ Page<T>๊ฐ€ ์‘๋‹ต
  4. ์‘๋‹ต๋œ Page<T>์— ๋‹ด๊ฒจ์ง„ Page ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋œ๋‹ค.

Pageable : ํŽ˜์ด์ง• ์ œ๊ณต ์ธํ„ฐํŽ˜์ด์Šค

Pageable ๋งŒ๋“œ๋Š” ๋ฒ•

๋”๋ณด๊ธฐ

PageRequest.of(int page, int size) : 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ์™€ ๊ฐœ์ˆ˜. ์ •๋ ฌ์ด ์ง€์ •๋˜์ง€ ์•Š์Œ
PageRequest.of(int page, int size, Sort sort) : ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ์™€ ๊ฐœ์ˆ˜, ์ •๋ ฌ ๊ด€๋ จ ์ •๋ณด
PageRequest.of(int page int size, Sort sort, Direction direction, String ... props) : 

0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ์™€ ๊ฐœ์ˆ˜, ์ •๋ ฌ์˜ ๋ฐฉํ–ฅ๊ณผ ์ •๋ ฌ ๊ธฐ์ค€ ํ•„๋“œ๋“ค

 

Pageable์˜ ๋ฉ”์„œ๋“œ

๋”๋ณด๊ธฐ

pageable.getTotalPages() : ์ด ํŽ˜์ด์ง€ ์ˆ˜
pageable.getTotalElements() : ์ „์ฒด ๊ฐœ์ˆ˜
pageable.getNumber() : ํ˜„์žฌ ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ
pageable.getSize() : ํŽ˜์ด์ง€ ๋‹น ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜
pageable.hasnext() : ๋‹ค์Œ ํŽ˜์ด์ง€ ์กด์žฌ ์—ฌ๋ถ€
pageable.isFirst() : ์‹œ์ž‘ํŽ˜์ด์ง€ ์—ฌ๋ถ€
pageable.getContent(), PageRequest.get() : 

์‹ค์ œ ์ปจํ…์ธ ๋ฅผ ๊ฐ€์ง€๊ณ  ์˜ค๋Š” ๋ฉ”์„œ๋“œ. getContext๋Š” List<Entity> ๋ฐ˜ํ™˜, get()์€ Stream<Entity> ๋ฐ˜ํ™˜

๋ฐ˜ํ™˜ ํƒ€์ž…

1) Page<T>

- ๊ฒŒ์‹œํŒ ํ˜•ํƒœ์˜ ํŽ˜์ด์ง•์—์„œ ์‚ฌ์šฉ

- ์ „์ฒด ์š”์†Œ ๊ฐœ์ˆ˜๋„ ํ•จ๊ผ ์กฐํšŒํ•œ๋‹ค

- ์‘๋‹ต ํ˜•ํƒœ

๋”๋ณด๊ธฐ

{
    "content": [
        {"id": 1, "username": "User 0", "address": "Korea", "age": 0},
        ...
        {"id": 5, "username": "User 4", "address": "Korea", "age": 4}
    ],
    "pageable": {
        "sort": {
            "sorted": false, // ์ •๋ ฌ ์ƒํƒœ
            "unsorted": true,
            "empty": true
        },
        "pageSize": 5, // ํŽ˜์ด์ง€ ํฌ๊ธฐ
        "pageNumber": 0, // ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ (0๋ฒˆ ๋ถ€ํ„ฐ ์‹œ์ž‘)
        "offset": 0, // ํ•ด๋‹น ํŽ˜์ด์ง€์˜ ์ฒซ๋ฒˆ์งธ ์š”์†Œ์˜ ์ „์ฒด ์ˆœ๋ฒˆ (๋‹ค์Œ ํŽ˜์ด์ง€์—์„œ๋Š” 5)
        "paged": true,
        "unpaged": false
    },
    "totalPages": 20, // ํŽ˜์ด์ง€๋กœ ์ œ๊ณต๋˜๋Š” ์ด ํŽ˜์ด์ง€ ์ˆ˜
    "totalElements": 100, // ๋ชจ๋“  ํŽ˜์ด์ง€์— ์กด์žฌํ•˜๋Š” ์ด ์›์†Œ ์ˆ˜
    "last": false,  // ๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€ ์—ฌ๋ถ€
    "number": 0,
    "sort": {
        "sorted": false,    // ์ •๋ ฌ ์‚ฌ์šฉ ์—ฌ๋ถ€
        "unsorted": true,
        "empty": true
    },
    "size": 5,       // Contents ์‚ฌ์ด์ฆˆ
    "numberOfElements": 5,  // Contents ์˜ ์›์†Œ ์ˆ˜
    "first": true,   // ์ฒซํŽ˜์ด์ง€ ์—ฌ๋ถ€
    "empty": false   // ๊ณต๋ฐฑ ์—ฌ๋ถ€
}

2) Slice<T>

- '๋”๋ณด๊ธฐ' ํ˜•ํƒœ์˜ ํŽ˜์ด์ง•์—์„œ ์‚ฌ์šฉ

- ์ „์ฒด ์š”์†Œ ๊ฐœ์ˆ˜ ๋Œ€์‹  offset ํ•„๋“œ๋กœ ์กฐํšŒ ๊ฐ€๋Šฅ ~  count ์ฟผ๋ฆฌ๊ฐ€ ์—†๊ณ  limit + 1 ์กฐํšŒ๋ฅผ ํ•œ๋‹ค(ํ˜„์—…์—์„œ ์ž˜ ์“ฐ์ง€ ์•Š๋Š”๋‹ค)

- ์‘๋‹ต ํ˜•ํƒœ

๋”๋ณด๊ธฐ

{
"content": [
  { "id": 13, "username": "User 12", "address": "Korea", "age": 12 },
  ...
  { "id": 16, "username": "User 15", "address": "Korea", "age": 15 }
],
"pageable": {
  "sort": { "sorted": false, "unsorted": true, "empty": true },
  "pageNumber": 3,
  "pageSize": 4,
  "offset": 12,
  "paged": true,
  "unpaged": false
},
"number": 3,
"numberOfElements": 4,
"first": false,
"last": false,
"size": 4,
"sort": { "sorted": false, "unsorted": true, "empty": true },
"empty": false
}

3) List<T> ํƒ€์ž…

์ „์ฒด ๋ชฉ๋ก๋ณด๊ธฐ ํ˜•ํƒœ์˜ ํŽ˜์ด์ง•์—์„œ ์‚ฌ์šฉ

๊ธฐ๋ณธ ํƒ€์ž…์œผ๋กœ Count ์กฐํšŒ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค

 

Sort : ์ •๋ ฌ

Sort ์‚ฌ์šฉ๋ฒ•

1) ์ปฌ๋Ÿผ ๊ฐ’์œผ๋กœ ์ •๋ ฌ : ๋ฉ”์„œ๋“œ์˜ ์ธ์ž ๊ฐ’์œผ๋กœ ์ปฌ๋Ÿผ์„ ๋„ฃ๊ณ , ๊ทธ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜๊ธฐ

๋”๋ณด๊ธฐ

Sort sort1 = Sort.by("name").descending();     // ๋‚ด๋ฆผ์ฐจ์ˆœ
Sort sort2 = Sort.by("password").ascending();  // ์˜ค๋ฆ„์ฐจ์ˆœ
Sort sortAll = sort1.and(sort2);      // 2๊ฐœ์ด์ƒ ๋‹ค์ค‘์ •๋ ฌ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค
Pageable pageable = PageRequest.of(0, 10, sortAll);  // pageable ์ƒ์„ฑ์‹œ ์ถ”๊ฐ€

2) ์ปฌ๋Ÿผ์ด ์•„๋‹Œ ๊ฐ’์œผ๋กœ ์ •๋ ฌ : @Query์œผ๋กœ Alias๋ฅผ ๊ฑธ์–ด์ค€ ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ

// ์•„๋ž˜์™€ ๊ฐ™์ด AS user_password ๋กœ Alias(AS) ๋ฅผ ๊ฑธ์–ด์ฃผ๋ฉด
@Query("SELECT u, u.password AS user_password FROM user u WHERE u.username = ?1")
List<User> findByUsername(String username, Sort sort);

// ์ด๋ ‡๊ฒŒ ํ•ด๋‹น user_password ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
List<User> users = findByUsername("user", Sort.by("user_password"));

3) SQL ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ ์ •๋ ฌ : JpaSort

// ์•„๋ž˜์™€ ๊ฐ™์ด ์ผ๋ฐ˜์ ์ธ ์ฟผ๋ฆฌ์—์„œ
@Query("SELECT u FROM user u WHERE u.username = ?1") // ์ด๊ฑด ์—†์–ด๋„๋จ
List<User> findByUsername(String username, Sort sort);

// ์ด๋ ‡๊ฒŒ ์ฟผ๋ฆฌํ•จ์ˆ˜ LENGTH() ์กฐ๊ฑด์„ ๊ฑธ์–ด์„œ password ๋ฌธ์ž๊ธธ์ด ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
List<User> users = findByUsername("user", JpaSort.unsafe("LENGTH(password)"));

 

์‹ค๋ฌด ํŒ

1. List<T>๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ LIst<T>๋กœ ๋ฐ›์„ ๊ฒƒ :

์ „์ฒด Count ์ฟผ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋กœ ๋ฐœ์ƒํ•˜๋Š” Page<T>๋ณด๋‹ค๋Š” List<T>๊ฐ€ ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋” ์•ˆ์ •์ ์ด๊ณ  ๋น ๋ฅด๋‹ค

 

2. Pageable๊ณผ ์‹ค์ œ ํŽ˜์ด์ง€ ์‚ฌ์ด -1 ๋ฌธ์ œ ํ•ด๊ฒฐํ•˜๊ธฐ : JPA ํŽ˜์ด์ง€๋Š” 0๋ถ€ํ„ฐ์ธ๋ฐ ํ™”๋ฉด์€ 1๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ๋ฌธ์ œ 

~ -1 ์ฒ˜๋ฆฌ๋ฅผ ์ค‘๋ณต์œผ๋กœ ํ•ด์ค˜์•ผํ•˜๋Š” ์ด์Šˆ

-> PageDTO๋ฅผ ๋งŒ๋“ค์–ด toPageable() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ž

 

3. Pageable์„ Get API์˜ ์š”์ฒญ ํ•„๋“œ๋กœ ๋ฐ›์•„์˜ค๊ธฐ

get API์—์„œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ Pageable๋กœ ๋ฐ›์œผ๋ฉด ๋ณ„๋„๋กœ @RequestParam์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ๋ฐ”๋กœ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค