์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

[์Šคํ”„๋ง 1ํŒ€] 8์žฅ. Spring Data JPA ํ™œ์šฉ

24-25/Spring 1

by oze 2025. 1. 3. 10:00

๋ณธ๋ฌธ

728x90

 

 

Spring Data JPA

๐Ÿ“Œ JPQL

JPQL์ด๋ž€ JPA Query Language์˜ ์ค„์ž„๋ง๋กœ, JPA์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฟผ๋ฆฌ์ด๋‹ค. 

SQL๊ณผ ๋ฌธ๋ฒ•์ด ๋งค์šฐ ๋น„์Šทํ•˜๋‹ค. SQL๊ณผ์˜ ์ฐจ์ด์ ์€ SQL์—์„œ๋Š” ํ…Œ์ด๋ธ”์ด๋‚˜ ์ปฌ๋Ÿผ์˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ JPQL์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฟผ๋ฆฌ์ด๊ธฐ์— ๋งคํ•‘๋œ ์—”ํ‹ฐํ‹ฐ์˜ ์ด๋ฆ„๊ณผ ํ•„๋“œ์˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

SELECT p FROM Product(์—”ํ‹ฐํ‹ฐ ํƒ€์ž…) p WHERE p.number(์—”ํ‹ฐํ‹ฐ ์†์„ฑ) = ?1;

 

 

๐Ÿ“Œ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋Š” JpaRepository๋ฅผ ์ƒ์†๋ฐ›๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ๋‹ค์–‘ํ•œ CRUD ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. 
ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ๋“ค์€ ์‹๋ณ„์ž ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑ๋˜๊ธฐ์— ๋ณ„๋„์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ์ด๋•Œ ๊ฐ„๋‹จํ•œ ์ฟผ๋ฆฌ๋ฌธ ์ž‘์„ฑ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์ด ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ์ด๋‹ค. 

 

 

๐Ÿ“Œ ํ€ด๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ๊ตฌ๋ถ„ 

  • ์ฃผ์ œ(Subject)
  • ์„œ์ˆ ์–ด(Predicate)

'find...By', 'exist...By'์™€ ๊ฐ™์€ ํ‚ค์›Œ๋“œ๋กœ ์ฟผ๋ฆฌ์˜ ์ฃผ์ œ๋ฅผ ์ •ํ•œ๋‹ค. 
'By'๋Š” ์„œ์ˆ ์–ด์˜ ์‹œ์ž‘์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ตฌ๋ถ„์ž ์—ญํ• ์ด๋‹ค. 
์„œ์ˆ ์–ด ๋ถ€๋ถ„์€ ๊ฒ€์ƒ‰ ๋ฐ ์ •๋ ฌ ์กฐ๊ฑด์„ ์ง€์ •ํ•˜๋Š” ์˜์—ญ์ด๋‹ค. ์—”ํ‹ฐํ‹ฐ ์†์„ฑ์„ ์ •์˜ํ•˜๊ณ , AND๋‚˜ OR์„ ์‚ฌ์šฉํ•ด ์กฐ๊ฑด ํ™•์žฅ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. 

 

์˜ˆ) (๋ฆฌํ„ดํƒ€์ž…) + {์ฃผ์ œ+์„œ์ˆ ์–ด(์†์„ฑ)} ๊ตฌ์กฐ์˜ ๋ฉ”์„œ๋“œ

List<Person> findByLastnameAndEmail(String lastName, String email);

 

์„œ์ˆ ์–ด์— ๋“ค์–ด๊ฐ€๋Š” ์—”ํ‹ฐํ‹ฐ ์†์„ฑ ์‹(Expression)์€ ์œ„์™€ ๊ฐ™์ด ์—”ํ‹ฐํ‹ฐ์—์„œ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋Š” ์†์„ฑ(ํ•„๋“œ)๋งŒ ์ฐธ์กฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. 

 

๐Ÿ“Œ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ์ฃผ์ œ ํ‚ค์›Œ๋“œ

  • find...By
  • read...By
  • get...By
  • query...By
  • search...By
  • stream...By

find...By

์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ‚ค์›Œ๋“œ์ด๋‹ค. '...'์œผ๋กœ ํ‘œ์‹œํ•œ ์˜์—ญ์—๋Š” ๋„๋ฉ”์ธ(์—”ํ‹ฐํ‹ฐ)์„ ํ‘œํ˜„ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ์ด๋ฏธ ๋„๋ฉ”์ธ์„ ์„ค์ •ํ•œ ํ›„์— ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๋ณต์œผ๋กœ ํŒ๋‹จํ•ด ์ƒ๋žตํ•˜๊ธฐ๋„ ํ•œ๋‹ค. ๋ฆฌํ„ด ํƒ€์ž…์œผ๋กœ Collection์ด๋‚˜ Stream์— ์†ํ•œ ํ•˜์œ„ ํƒ€์ž… ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

// ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ์ž‘์„ฑ
Optional<Product> findByNumber(Long number);
List<Product> findAllByName(String name);
Product queryByNumber(Long number);

exists...By

ํŠน์ • ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ‚ค์›Œ๋“œ์ด๋‹ค. ๋ฆฌํ„ด ํƒ€์ž…์œผ๋กœ๋Š” boolean์„ ์‚ฌ์šฉํ•œ๋‹ค.

boolean existsByNumber(Long number);

count...By

์กฐํšŒ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ ํ›„ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋กœ ๋‚˜์˜จ ๋ ˆ์ฝ”๋“œ ๊ฐœ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค. 

long countByName(String name);

delete...By, remove...By

์‚ญ์ œ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๋ฆฌํ„ด ํƒ€์ž…์ด ์—†๊ฑฐ๋‚˜ ์‚ญ์ œํ•œ ํšŸ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

void deleteByNumber(Long number);
long removeByName(String name);

...First<number.>..., ...Top<number.>...

์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด ์กฐํšŒ๋œ ๊ฒฐ๊ด๊ฐ’์˜ ๊ฐœ์ˆ˜๋ฅผ ์ œํ•œํ•˜๋Š” ํ‚ค์›Œ๋“œ์ด๋‹ค. ๋‘ ํ‚ค์›Œ๋“œ๋Š” ๋™์ผํ•œ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ฃผ์ œ์™€ By ์‚ฌ์ด์— ์œ„์น˜ํ•œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ํ•œ ๋ฒˆ์˜ ๋™์ž‘์œผ๋กœ ์—ฌ๋Ÿฌ ๊ฑด์„ ์กฐํšŒํ•  ๋•Œ ์‚ฌ์šฉํ•˜๊ณ , ๋‹จ ๊ฑด ์กฐํšŒ๋Š” <number.>๋ฅผ ์ƒ๋žตํ•œ๋‹ค.

List<Product> findFirst5ByName(String name);
List<Product> findTop100ByName(String name);

 

 

๐Ÿ“Œ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ์กฐ๊ฑด์ž ํ‚ค์›Œ๋“œ

JPQL์˜ ์„œ์ˆ ์–ด ๋ถ€๋ถ„์—์„œ ์‚ฌ์šฉํ•œ๋‹ค. 

Is

๊ฐ’์˜ ์ผ์น˜๋ฅผ ์กฐ๊ฑด์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์กฐ๊ฑด์ž ํ‚ค์›Œ๋“œ์ด๋‹ค. ์ƒ๋žต๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์œผ๋ฉฐ Equals์™€ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. 

// findByNumber ๋ฉ”์„œ๋“œ์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘
Product findByNumberIs(Long number); 
Product findByNumberEquals(Long number);

(Is)Not

๊ฐ’์˜ ๋ถˆ์ผ์น˜๋ฅผ ์กฐ๊ฑด์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์กฐ๊ฑด์ž ํ‚ค์›Œ๋“œ์ด๋‹ค. Is๋Š” ์ƒ๋žตํ•˜๊ณ  Not ํ‚ค์›Œ๋“œ๋งŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

Product findByNumberIsNot(Long number);
Product findByNumberNot(Long number);

(Is)Null, (Is)NotNull

๊ฐ’์ด null์ธ์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ์กฐ๊ฑด์ž ํ‚ค์›Œ๋“œ์ด๋‹ค.

List<Product> findByUpdatedAtNull();
List<Product> findByUpdatedAtIsNull();
List<Product> findByUpdatedAtNotNull();
List<Product> findByUpdatedAtIsNotNull();

(Is)True, (Is)False

boolean ํƒ€์ž…์œผ๋กœ ์ง€์ •๋œ ์นผ๋Ÿผ๊ฐ’์„ ํ™•์ธํ•˜๋Š” ํ‚ค์›Œ๋“œ์ด๋‹ค.

Product findByisActiveTrue();
Product findByisActiveIsTrue();
Product findByisActiveFalse();
Product findByisActiveIsFalse();

And, Or

์—ฌ๋Ÿฌ ์กฐ๊ฑด์„ ๋ฌถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

Product findByNumberAndName(Long number, String name);
Product findByNumberOrName(Long number, String name);

(Is)Greater Than, (Is)LessThan, (Is)Between

์ˆซ์ž๋‚˜ datetime ์ปฌ๋Ÿผ์„ ๋Œ€์ƒ์œผ๋กœ ํ•œ ๋น„๊ต ์—ฐ์‚ฐ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์กฐ๊ฑด์ž ํ‚ค์›Œ๋“œ์ด๋‹ค.
GreaterThan, LessThan ํ‚ค์›Œ๋“œ๋Š” ๋น„๊ต ๋Œ€์ƒ์— ๋Œ€ํ•œ ์ดˆ๊ณผ/๋ฏธ๋งŒ์˜ ๊ฐœ๋…์œผ๋กœ ๋น„๊ต ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ฒฝ๊ณ—๊ฐ’์„ ํฌํ•จํ•˜๋ ค๋ฉด Equal ํ‚ค์›Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. 

List<Product> findByPriceIsGreaterThan(Long price);
List<Product> findByPriceGreaterThan(Long price);
List<Product> findByPriceGreaterThanEqual(Long price);
List<Product> findByPriceIsLessThan(Long price);
List<Product> findByPriceLessThan(Long price);
List<Product> findByPriceLessThanEqual(Long price);
List<Product> findByPriceIsBetween(Long lowPrice, Long highPrice);
List<Product> findByPriceBetween(Long lowPrice, Long highPrice);

(Is)StartingWith(==StartsWith), (Is)EndingWith(==EndsWith), (Is)Containing(==Contains), (Is)Like

์ปฌ๋Ÿผ๊ฐ’์—์„œ ์ผ๋ถ€ ์ผ์น˜ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ์กฐ๊ฑด์ž ํ‚ค์›Œ๋“œ์ด๋‹ค. 
SQL ์ฟผ๋ฆฌ๋ฌธ์—์„œ ๊ฐ’์˜ ์ผ๋ถ€๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฐ’์„ ์ถ”์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” '%' ํ‚ค์›Œ๋“œ์™€ ๋™์ผํ•œ ์—ญํ• ์„ ํ•˜๋Š” ํ‚ค์›Œ๋“œ์ด๋‹ค.
์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” SQL๋ฌธ์„ ๋ณด๋ฉด Containing ํ‚ค์›Œ๋“œ๋Š” ๋ฌธ์ž์—ด์˜ ์–‘ ๋, StartingWith ํ‚ค์›Œ๋“œ๋Š” ๋ฌธ์ž์—ด์˜ ์•ž, EndingWith ํ‚ค์›Œ๋“œ๋Š” ๋ฌธ์ž์—ด์˜ ๋์— '%'๊ฐ€ ๋ฐฐ์น˜๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ๋ณ„๋„๋กœ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ํ‚ค์›Œ๋“œ๋Š” Like ํ‚ค์›Œ๋“œ์ธ๋ฐ, ์ด ํ‚ค์›Œ๋“œ๋Š” ์ฝ”๋“œ ์ˆ˜์ค€์—์„œ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ์ „๋‹ฌํ•˜๋Š” ๊ฐ’์— %๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ž…๋ ฅํ•ด์•ผ ํ•œ๋‹ค. 

List<Product> findByNameLike(String name);
List<Product> findByNameIsLike(String name);

List<Product> findByNameContains(String name);
List<Product> findByNameContaining(String name);
List<Product> findByNameIsContaining(String name);

List<Product> findByNameStartsWith(String name);
List<Product> findByNameStartingWith(String name);
List<Product> findByNameIsStartingWith(String name);

List<Product> findByNameEndsWith(String name);
List<Product> findByNameEndingWith(String name);
List<Product> findByNameIsEndingWith(String name);

 

 

๐Ÿ“Œ ์ •๋ ฌ ์ฒ˜๋ฆฌ

ORDER BY

๊ธฐ๋ณธ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ์ž‘์„ฑ ํ›„ OrderBy ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฝ์ž…ํ•œ๋‹ค. ์ •๋ ฌํ•˜๊ณ ์ž ํ•˜๋Š” ์นผ๋Ÿผ๊ณผ ์˜ค๋ฆ„์ฐจ์ˆœ/๋‚ด๋ฆผ์ฐจ์ˆœ์„ ์„ค์ •ํ•œ๋‹ค. 

List<Product> findByNameOrderByNumberAsc(String name); // Asc : ์˜ค๋ฆ„์ฐจ์ˆœ // ์ƒํ’ˆ์ •๋ณด๋ฅผ ์ด๋ฆ„์œผ๋กœ ๊ฒ€์ƒ‰ํ•œ ํ›„ ์ƒํ’ˆ ๋ฒˆํ˜ธ๋กœ ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ
List<Product> findByNameOrderByNumberDesc(String name); // Desc : ๋‚ด๋ฆผ์ฐจ์ˆœ

์—ฌ๋Ÿฌ ๊ฐœ์˜ ์กฐ๊ฑด ์‚ฌ์šฉ ์‹œ, And๋‚˜ Or ํ‚ค์›Œ๋“œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. 

List<Product> findByNameOrderByPriceAscStockDesc(String name); // ๋จผ์ € Price๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ ํ›„ ํ›„์ˆœ์œ„๋กœ ์žฌ๊ณ ์ˆ˜๋Ÿ‰์„ ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ ์ˆ˜ํ–‰

๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ™œ์šฉํ•œ ์ฟผ๋ฆฌ ์ •๋ ฌ์ด๋‹ค. 

List<Product> findByName(String name, Sort sort);

์ด๋ฆ„์— ํ‚ค์›Œ๋“œ๋ฅผ ๋„ฃ์ง€ ์•Š๊ณ  Sort ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•ด ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„๋“ค์ธ ์ •๋ ฌ ๊ธฐ์ค€์„ ๊ฐ€์ง€๊ณ  ์ฟผ๋ฆฌ๋ฌธ์„ ์ž‘์„ฑํ•œ๋‹ค. ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜๋Š” ๋‹จ๊ณ„์—์„œ ์ฝ”๋“œ๊ฐ€ ์ค„์–ด๋“œ๋Š” ์žฅ์ ์ด ์žˆ๋‹ค. 

 

๐Ÿ“Œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

@SpringBootTest
public class ProductRepositoryTest0 {

    @Autowired
    ProductRepository productRepository;

    @Test
    void sortingAndPagingTest() {
        Product product1 = new Product();
        product1.setName("ํŽœ");
        product1.setPrice(1000);
        product1.setStock(100);
        product1.setCreatedAt(LocalDateTime.now());
        product1.setUpdatedAt(LocalDateTime.now());

        Product product2 = new Product();
        product2.setName("ํŽœ");
        product2.setPrice(5000);
        product2.setStock(300);
        product2.setCreatedAt(LocalDateTime.now());
        product2.setUpdatedAt(LocalDateTime.now());

        Product product3 = new Product();
        product3.setName("ํŽœ");
        product3.setPrice(500);
        product3.setStock(50);
        product3.setCreatedAt(LocalDateTime.now());
        product3.setUpdatedAt(LocalDateTime.now());

        Product savedProduct1 = productRepository.save(product1);
        Product savedProduct2 = productRepository.save(product2);
        Product savedProduct3 = productRepository.save(product3);

        productRepository.findByName("ํŽœ", Sort.by(Sort.Order.asc("price")));
        productRepository.findByName("ํŽœ", Sort.by(Sort.Order.asc("price"), Sort.Order.desc("stock")));

    }

}

 

Sort ๋ถ€๋ถ„์„ ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ๋กœ ๋ถ„๋ฆฌํ•ด ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•

    ...์ƒ๋‹จ ์ฝ”๋“œ ๋™์ผ, ์ƒ๋žต

        productRepository.findByName("ํŽœ", getSort());

    }

    private Sort getSort() {
        return Sort.by(
                Sort.Order.asc("price"),
                Sort.Order.desc("stock")
        );
    }

 

 

 

 

ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ

๐Ÿ“Œ ํŽ˜์ด์ง•์ด๋ž€?

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ๊ฐœ์ˆ˜๋กœ ๋‚˜๋ˆ  ํŽ˜์ด์ง€๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

ex) 25๊ฐœ ๋ ˆ์ฝ”๋“œ -> ๋ ˆ์ฝ”๋“œ๋ฅผ 7๊ฐœ์”ฉ, ์ด 4๊ฐœ์˜ ํŽ˜์ด์ง€๋กœ ๊ตฌ๋ถ„ / ํŠน์ • ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. 

 

JPA์—์„œ๋Š” ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด Page์™€ Pageable๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. 

// ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ
Page<Product> findByName(String name, Pageable pageable);

๋ฆฌํ„ด ํƒ€์ž…์œผ๋กœ Page๋ฅผ ์„ค์ •ํ•˜๊ณ  ๋งค๊ฐœ๋ณ€์ˆ˜์—๋Š” Pageable ํƒ€์ž…์˜ ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•œ๋‹ค. 

// ํŽ˜์ด์ง• ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
Page<Product> productPage = productRepository.findByName("ํŽœ", PageRequest.of(0, 2));

๋ฆฌํ„ด ํƒ€์ž…์œผ๋กœ Page ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„์•ผ ํ•˜๊ธฐ์— Page<Product>๋กœ ํƒ€์ž…์„ ์„ ์–ธํ•ด ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ด์„ ๋ฐ›๋Š”๋‹ค. Pageable ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด PageRequest ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. PageRequest๋Š” Pageable์˜ ๊ตฌํ˜„์ฒด์ด๋‹ค. 

 

์ผ๋ฐ˜์ ์œผ๋กœ PageRequest๋Š” of ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด PageRequest ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. of ๋ฉ”์„œ๋“œ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ ํ˜•ํƒœ๋กœ ์˜ค๋ฒ„๋กœ๋”ฉํ•œ๋‹ค. 

  • of(int page, int size): ๋งค๊ฐœ๋ณ€์ˆ˜[ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ(0๋ถ€ํ„ฐ ์‹œ์ž‘), ํŽ˜์ด์ง€๋‹น ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜] / ๋ฐ์ดํ„ฐ ์ •๋ ฌ X
  • of(int page, int size, Sort): ๋งค๊ฐœ๋ณ€์ˆ˜[ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ, ํŽ˜์ด์ง€๋‹น ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜, ์ •๋ ฌ] / sort์— ์˜ํ•ด ์ •๋ ฌ
  • of(int page, int size, Direction, String... properties): ๋งค๊ฐœ๋ณ€์ˆ˜[ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ, ํŽ˜์ด์ง€๋‹น ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜, ์ •๋ ฌ ๋ฐฉํ–ฅ, ์†์„ฑ(์ปฌ๋Ÿผ)] / Sort.by(direction, properties)์— ์˜ํ•ด ์ •๋ ฌ

 

๐Ÿ“Œ Page ๊ฐ์ฒด์˜ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ

Page ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅํ•˜๋ฉด ํ•ด๋‹น ๊ฐ์ฒด์˜ ๊ฐ’์„ ๋ณด์—ฌ์ฃผ์ง€ ์•Š๊ณ  ๋ช‡ ๋ฒˆ์งธ ํŽ˜์ด์ง€์— ํ•ด๋‹นํ•˜๋Š”์ง€๋งŒ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. 

Page<Product> productPage = productRepository.findByName("ํŽœ", PageRequest.of(0, 2));
        System.out.println(productPage.getContent()); // ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๊ฐ’ ์ถœ๋ ฅ




@Query ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉํ•˜๊ธฐ

@Query ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด ์ง์ ‘ JPQL์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

๐Ÿ“Œ @Query ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ

    @Query("SELECT p FROM Product AS p WHERE p.name = ?1")
    List<Product> findByName(String name);

FROM ๋’ค์—์„œ ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…์„ ์ง€์ •ํ•œ๋‹ค. 

๋ณ„์นญ์„ ์ƒ์„ฑํ•œ๋‹ค. (AS ์ƒ๋žต ๊ฐ€๋Šฅ)
WHERE๋ฌธ์—์„œ๋Š” SQL๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์กฐ๊ฑด์„ ์ง€์ •ํ•œ๋‹ค. 

 

์กฐ๊ฑด๋ฌธ์—์„œ ์‚ฌ์šฉํ•œ '?1'์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›๊ธฐ ์œ„ํ•œ ์ธ์ž์— ํ•ด๋‹นํ•œ๋‹ค.

1์€ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์˜ค๋ฅ˜ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด @Param ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. 

    @Query("SELECT p FROM Product AS p WHERE p.name = :name")
    List<Product> findByNameParam(@Param("name") String name);

ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด ๋†’์•„์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์ˆ˜์›”ํ•ด์ง„๋‹ค. 

 

 

๐Ÿ“Œ ํŠน์ • ์ปฌ๋Ÿผ๋งŒ ์ถ”์ถœํ•˜๋Š” ์ฟผ๋ฆฌ

@Query๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…์ด ์•„๋‹ˆ๋ผ ์›ํ•˜๋Š” ์ปฌ๋Ÿผ์˜ ๊ฐ’๋งŒ ์ถ”์ถœ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. 

    @Query("SELECT p.name, p.price, p.stock FROM Product p WHERE p.name = :name")
    List<Object[]> findByNameParam2(@Param("name") String name);

SELECT์— ๊ฐ€์ ธ์˜ค๊ณ ์ž ํ•˜๋Š” ์ปฌ๋Ÿผ์„ ์ง€์ •ํ•œ๋‹ค. ์ด๋•Œ ๋ฉ”์„œ๋“œ์—์„œ๋Š” Object ๋ฐฐ์—ด์˜ ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ๋ฆฌํ„ด ํƒ€์ž…์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค. 




QueryDSL ์ ์šฉ ๋ฐ ํ™œ์šฉํ•˜๊ธฐ

๐Ÿ“Œ QueryDSL ์ ์šฉํ•˜๊ธฐ

๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” JPQL์˜ ํ•œ๊ณ„๋Š” @Query ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ๋Œ€๋ถ€๋ถ„ ํ•ด์†Œํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ง์ ‘ ๋ฌธ์ž์—ด์„ ์ž…๋ ฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ปดํŒŒ์ผ ์‹œ์ ์— ์—๋Ÿฌ๋ฅผ ์žก์ง€ ๋ชปํ•˜๊ณ  ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. 
์ฟผ๋ฆฌ์˜ ๋ฌธ์ž์—ด์ด ์ž˜๋ชป๋œ ๊ฒฝ์šฐ์—๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋œ ํ›„ ๋กœ์ง์ด ์‹คํ–‰๋˜๊ณ  ๋‚˜์„œ์•ผ ์˜ค๋ฅ˜ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋œ๋‹ค. 

์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์–ด ๋ณด์ด๋‹ค๊ฐ€ ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•˜๊ณ  ๋‚˜์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ๊ฒฌ๋˜๋Š” ๋ฆฌ์Šคํฌ๊ฐ€ ์žˆ๋‹ค. 

์ด ๊ฐ™์€ ๋ฌธ์ œ ํ•ด๊ฒฐ์„ ์œ„ํ•ด QueryDSL ์‚ฌ์šฉํ•œ๋‹ค. 
QueryDSL์€ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ ์ฝ”๋“œ๋กœ ์ฟผ๋ฆฌ ์ž‘์„ฑ์„ ๋„์™€์ค€๋‹ค. 

 

๐Ÿ“Œ QueryDSL์ด๋ž€?

์ •์  ํƒ€์ž…์„ ์ด์šฉํ•ด SQL๊ณผ ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ. ๋ฌธ์ž์—ด์ด๋‚˜ XML ํŒŒ์ผ์„ ํ†ตํ•ด ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹  QueryDSL์ด ์ œ๊ณตํ•˜๋Š” Fluent API๋ฅผ ํ™œ์šฉํ•ด ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. 

 

๐Ÿ“Œ QueryDSL ์žฅ์ 

  • IDE๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ฝ”๋“œ ์ž๋™ ์™„์„ฑ ๊ธฐ๋Šฅ ์‚ฌ์šฉ
  • ๋ฌธ๋ฒ•์ ์œผ๋กœ ์ž˜๋ชป๋œ ์ฟผ๋ฆฌ ํ—ˆ์šฉ X -> ๋ฌธ๋ฒ•์˜ค๋ฅ˜ X
  • ๊ณ ์ •๋œ SQL ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋™์  ์ฟผ๋ฆฌ ์ƒ์„ฑ ๊ฐ€๋Šฅ
  • ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•˜๋ฏ€๋กœ ๊ฐ€๋…์„ฑ, ์ƒ์‚ฐ์„ฑ ํ–ฅ์ƒ
  • ๋„๋ฉ”์ธ ํƒ€์ž…๊ณผ ํ”„๋กœํผํ‹ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ฐธ์กฐ ๊ฐ€๋Šฅ

 

pom.xml

1. ์˜์กด์„ฑ ์ถ”๊ฐ€

        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
        </dependency>

2. APT ํ”Œ๋Ÿฌ๊ทธ์ธ ์ถ”๊ฐ€

            <plugin>
                <groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>1.1.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/generated-sources/java</outputDirectory>
                            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                            <options>
                                <querydsl.entityAccessors>true</querydsl.entityAccessors>
                            </options>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
  • JPAAnnotationProcessor๋Š” @Entity ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์ •์˜๋œ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๋ฅผ ์ฐพ์•„์„œ ์ฟผ๋ฆฌ ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค. 

 

3. ๋ฉ”์ด๋ธ์˜ compile ๋‹จ๊ณ„๋ฅผ ํด๋ฆญํ•ด ๋นŒ๋“œ ์ž‘์—… ์ˆ˜ํ–‰

๋นŒ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ์œ„์—์„œ ์ž‘์„ฑํ–ˆ๋˜ outputDirectory์— ์„ค์ •ํ•œ generated-source ๊ฒฝ๋กœ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด Q๋„๋ฉ”์ธ ํด๋ž˜์Šค๊ฐ€ ์ƒ์„ฑ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

 

4. IntelliJ์—์„œ [File] -> [Project Structure]
[Mark as]์˜ [Sources] ๋ˆŒ๋Ÿฌ IDE์—์„œ ์†Œ์ŠคํŒŒ์ผ๋กœ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ค์ •ํ•œ๋‹ค. 

QueryDSL ์ ์šฉ์ด ์™„๋ฃŒ๋œ ๋ชจ์Šต์ด๋‹ค. 

 

QueryDSL์€ ์ง€๊ธˆ๊นŒ์ง€ ์ž‘์„ฑํ–ˆ๋˜ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค์™€ Q๋„๋ฉ”์ธ์ด๋ผ๋Š” ์ฟผ๋ฆฌ ํƒ€์ž…์˜ ํด๋ž˜์Šค๋ฅผ ์ž์ฒด์ ์œผ๋กœ ์ƒ์„ฑํ•ด์„œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด SQL๊ณผ ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ด ์ œ๊ณตํ•œ๋‹ค. 



QueryDSL ์‚ฌ์šฉํ•˜๊ธฐ

๐Ÿ“Œ JPAQuery๋ฅผ ํ™œ์šฉํ•œ QueryDSL

QueryDSL์— ์˜ํ•ด ์ƒ์„ฑ๋œ Q๋„๋ฉ”์ธ ํด๋ž˜์Šค๋ฅผ ํ™œ์šฉํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

    @PersistenceContext
    EntityManager entityManager;

    @Test
    void queryDslTest() {
        JPAQuery<Product> query = new JPAQuery(entityManager);

        QProduct qProduct = QProduct.product;

        List<Product> productList = query
                .from(qProduct)
                .where(qProduct.name.eq("ํŽœ"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (Product product : productList) {
            System.out.println("----------------");
            System.out.println();
            System.out.println("Product Number : " + product.getNumber());
            System.out.println("Product Name : " + product.getName());
            System.out.println("Product Price : " + product.getPrice());
            System.out.println("Product Stock : " + product.getStock());
            System.out.println();
            System.out.println("----------------");
        }
    }
  • QueryDSL์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” JPAQuery ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • JPAQuery๋Š” ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €(EntityManager)๋ฅผ ํ™œ์šฉํ•ด ์ƒ์„ฑํ•œ๋‹ค.
  • JPAQuery๋Š” ๋นŒ๋” ํ˜•์‹์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. 

 

List ํƒ€์ž…์œผ๋กœ ๊ฐ’์„ ๋ฆฌํ„ด ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ๋Š” fetch() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

  • List<T.> fetch(): ์กฐํšŒ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฐ˜ํ™˜
  • T fetchOne: ๋‹จ ๊ฑด์˜ ์กฐํšŒ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜
  • T fetchFirst(): ์—ฌ๋Ÿฌ ๊ฑด์˜ ์กฐํšŒ ๊ฒฐ๊ณผ ์ค‘ 1๊ฑด ๋ฐ˜ํ™˜. ๋‚ด๋ถ€ ๋กœ์ง - .limit(1).fetchOne()
  • Long fetchCount(): ์กฐํšŒ ๊ฒฐ๊ณผ ๊ฐœ์ˆ˜ ๋ฐ˜ํ™˜
  • QueryResult<T.> fetchResults(): ์กฐํšŒ ๊ฒฐ๊ณผ ๋ฆฌ์ŠคํŠธ์™€ ๊ฐœ์ˆ˜๋ฅผ ํฌํ•จํ•œ QueryResults ๋ฐ˜ํ™˜

 

๐Ÿ“Œ JPAQueryFactory ํ™œ์šฉ

    @Test
    void queryDslTest2() {
        JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
        QProduct qProduct = QProduct.product;

        List<Product> productList = jpaQueryFactory.selectFrom(qProduct)
                .where(qProduct.name.eq("ํŽœ"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (Product product : productList) {
            System.out.println("----------------");
            System.out.println();
            System.out.println("Product Number : " + product.getNumber());
            System.out.println("Product Name : " + product.getName());
            System.out.println("Product Price : " + product.getPrice());
            System.out.println("Product Stock : " + product.getStock());
            System.out.println();
            System.out.println("----------------");
        }
    }
  • JPAQueryFactory๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด select์ ˆ๋ถ€ํ„ฐ ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๋งŒ์•ฝ ์ „์ฒด ์ปฌ๋Ÿผ์„ ์กฐํšŒํ•˜์ง€ ์•Š๊ณ  ์ผ๋ถ€๋งŒ ์กฐํšŒํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด selectFrom()์ด ์•„๋‹Œ select()์™€ from() ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌ๋ถ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. 

    @Test
    void queryDslTest3() {
        JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
        QProduct qProduct = QProduct.product;

        // select ๋Œ€์ƒ์ด ํ•˜๋‚˜์ธ ๊ฒฝ์šฐ
        List<String> productList = jpaQueryFactory
                .select(qProduct.name)
                .from(qProduct)
                .where(qProduct.name.eq("ํŽœ"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (String product : productList) {
            System.out.println("----------------");
            System.out.println("Product Name : " + product);
            System.out.println("----------------");
        }

        // select ๋Œ€์ƒ์ด ์—ฌ๋Ÿฌ ๊ฐœ์ผ ๊ฒฝ์šฐ (,)๋กœ ๊ตฌ๋ถ„ํ•ด์„œ ์ž‘์„ฑ
        List<Tuple> tupleList = jpaQueryFactory
                .select(qProduct.name, qProduct.price)
                .from(qProduct)
                .where(qProduct.name.eq("ํŽœ"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (Tuple product : tupleList) {
            System.out.println("----------------");
            System.out.println("Product Name : " + product.get(qProduct.name));
            System.out.println("Product Name : " + product.get(qProduct.price));
            System.out.println("----------------");
        }
    }

 

๐Ÿ“Œ QueryDSL์„ ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ํ™œ์šฉ

์ปจํ”ผ๊ทธ ํด๋ž˜์Šค ์ƒ์„ฑ

@Configuration
public class QueryDSLConfiguration {

    @PersistenceContext
    EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }

}

JPAQueryFactory ๊ฐ์ฒด๋ฅผ @Bean ๊ฐ์ฒด๋กœ ๋“ฑ๋กํ•ด ๋‘๋ฉด ๋งค๋ฒˆ JPAQueryFactory๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š๊ณ  ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์—์„œ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ๋‹ค. 

 

๐Ÿ“Œ JPAQueryFactory ๋นˆ์„ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

    @Autowired
    JPAQueryFactory jpaQueryFactory;

    @Test
    void queryDSLTest4() {
        QProduct qProduct = QProduct.product;

        List<String> productList = jpaQueryFactory
                .select(qProduct.name)
                .from(qProduct)
                .where(qProduct.name.eq("ํŽœ"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (String product : productList) {
            System.out.println("----------------");
            System.out.println("Product Name : " + product);
            System.out.println("----------------");
        }
    }

 

๐Ÿ“Œ QuerydslPredicateExecutor, QuerydslRepositorySupport ํ™œ์šฉ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA์—์„œ๋Š” QueryDSL์„ ๋”์šฑ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ QuerydslPredicateExecutor ์ธํ„ฐํŽ˜์ด์Šค์™€ QuerydslRepositorySupport ํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

QuerydslPredicateExecutor

JpaRepository์™€ ํ•จ๊ป˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ QueryDSL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

QuerydslPredicateExecutor๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

public interface QProductRepository extends JpaRepository<Product, Long>, QuerydslPredicateExecutor<Product> {

}

 

QuerydslPredicateExecutor์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ

  • Optional<T.> findOne(Predicate predicate);
  • Iterable<T.> findAll(Predicate predicate);
  • Iterable<T.> findAll(Predicate predicate, Sort sort);
  • Iterable<T.> findAll(Predicate predicate, OrderSpecifier<?>... orders);
  • Iterable<T.> findAll(OrderSpecifier<?>...orders);
  • Page<T.> findAll(Predicate predicate, Pageable pageable);
  • long count(Predicate predicate);
  • boolean exists(Predicate predicate);

๋Œ€๋ถ€๋ถ„ Predicate ํƒ€์ž…์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค.
Predicate๋Š” ํ‘œํ˜„์‹์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ QueryDSL์—์„œ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค.

 

๐Ÿ“Œ Predicate๋ฅผ ํ™œ์šฉํ•œ findOne() ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

@SpringBootTest
public class QProductRepositoryTest {

    @Autowired
    QProductRepository qProductRepository;

    @Test
    public void queryDSLTest1() {
        Predicate predicate = QProduct.product.name.containsIgnoreCase("ํŽœ")
                .and(QProduct.product.price.between(1000, 2500));

        Optional<Product> foundProduct = qProductRepository.findOne(predicate);

        if (foundProduct.isPresent()) {
            Product product = foundProduct.get();
            System.out.println(product.getNumber());
            System.out.println(product.getName());
            System.out.println(product.getPrice());
            System.out.println(product.getStock());
        }
    }

}

Predicate๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ํ‘œํ˜„์‹์œผ๋กœ ์ •์˜ํ•˜๋Š” ์ฟผ๋ฆฌ์ด๋‹ค.
์œ„๋Š” Predicate๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜ํ•˜๊ณ  ์‚ฌ์šฉํ–ˆ์ง€๋งŒ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„œ์ˆ ๋ถ€๋งŒ ๊ฐ€์ ธ๋‹ค ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. 

    @Test
    public void queryDSLTest2() {
        QProduct qProduct = QProduct.product;

        Iterable<Product> productList = qProductRepository.findAll(
                qProduct.name.contains("ํŽœ")
                        .and(qProduct.price.between(550, 1500))
        );

        for (Product product : productList) {
            System.out.println(product.getNumber());
            System.out.println(product.getName());
            System.out.println(product.getPrice());
            System.out.println(product.getStock());
        }
    }

QuerydslPredicateExecutor๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋”์šฑ ํŽธํ•˜๊ฒŒ QueryDSL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ join์ด๋‚˜ fetch ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋‹จ์ ์ด ์กด์žฌํ•œ๋‹ค. 

 

๐Ÿ“Œ QueryRepositorySupport ์ถ”์ƒ ํด๋ž˜์Šค ์‚ฌ์šฉํ•˜๊ธฐ

๊ฐ€์žฅ ๋ณดํŽธ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์€ CustomRepository๋ฅผ ํ™œ์šฉํ•ด ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. 

  • JpaRepository๋ฅผ ์ƒ์†๋ฐ›๋Š” ProductRepository ์ƒ์„ฑ
  • ์ง์ ‘ ๊ตฌํ˜„ํ•œ ์ฟผ๋ฆฌ ์‚ฌ์šฉ์„ ์œ„ํ•ด JpaRepository๋ฅผ ์ƒ์†๋ฐ›์ง€ ์•Š๋Š” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค์ธ ProductRepositoryCustom ์ƒ์„ฑ. ์ด ์ธํ„ฐํŽ˜์ด์Šค์— ์ •์˜ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ธฐ๋Šฅ๋“ค์„ ๋ฉ”์„œ๋“œ๋กœ ์ •์˜
  • ProductRepositoryCustom์—์„œ ์ •์˜๋œ ๋ฉ”์„œ๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹ค์ œ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๊ตฌํ˜„์ฒด์ธ ProductRepositoryCustomImpl ํด๋ž˜์Šค ์ƒ์„ฑ
  • ProductRepositoryCustomImpl ํด๋ž˜์Šค์—์„œ๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ฟผ๋ฆฌ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ QueryDSL ์‚ฌ์šฉ์„ ์œ„ํ•ด QueryDslRepositorySupport ์ƒ์†๋ฐ›์Œ

 

๐Ÿ“Œ ์˜ˆ์‹œ ์ฝ”๋“œ

ProductRepositoryCustom
์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ฟผ๋ฆฌ๋กœ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค.

public interface ProductRepositoryCustom {

    List<Product> findByName(String name);

}

 

 

ProductRepositoryCustom ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด์ธ ProductRepositoryCustomImpl ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

QueryDSL ์‚ฌ์šฉ์„ ์œ„ํ•ด QuerydslRepositorySupport๋ฅผ ์ƒ์†๋ฐ›๊ณ  ProductRepositoryCustom ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ํ•œ๋‹ค. QuerydslRepositorySupport๋ฅผ ์ƒ์†๋ฐ›์œผ๋ฉด ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๋„๋ฉ”์ธ ํด๋ž˜์Šค๋ฅผ ๋ถ€๋ชจ ํด๋ž˜์Šค์— ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.
์ธํ„ฐํŽ˜์ด์Šค์— ์ •์˜ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. 

@Component
public class ProductRepositoryCustomImpl extends QuerydslRepositorySupport implements ProductRepositoryCustom {

    public ProductRepositoryCustomImpl() {
        super(Product.class);
    }

    // ์ธํ„ฐํŽ˜์ด์Šค์— ์ •์˜ํ•œ ๋ฉ”์„œ๋“œ ๊ตฌํ˜„
    @Override
    public List<Product> findByName(String name) {
        QProduct product = QProduct.product;

        List<Product> productList = from(product)
                .where(product.name.eq(name))
                .select(product)
                .fetch();

        return productList;
    }
}

 

ProductRepository ์ธํ„ฐํŽ˜์ด์Šค

๊ธฐ์กด์— ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•˜๊ฒŒ JpaRepository๋ฅผ ์ƒ์†๋ฐ›๋Š”๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ JpaRepository์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ๋ณ„๋„๋กœ ProductRepositoryCustom ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ •์˜ํ•œ ๋ฉ”์„œ๋“œ๋„ ๊ตฌํ˜„์ฒด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

@Repository("productRepositorySupport")
public interface ProductRepository extends JpaRepository<Product, Long>, ProductRepositoryCustom {

}

 

๐Ÿ“Œ ProductRepository์˜ findByName() ๋ฉ”์„œ๋“œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

@SpringBootTest
public class ProductRepositoryTest {

    @Autowired
    ProductRepository productRepository;

    @Test
    void findByNameTest() {
        List<Product> productList = productRepository.findByName("ํŽœ");

        for (Product product : productList) {
            System.out.println(product.getNumber());
            System.out.println(product.getName());
            System.out.println(product.getPrice());
            System.out.println(product.getStock());
        }
    }

}

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•˜๋ฉด์„œ ๋ชจ๋“  ๋กœ์ง์„ ๊ตฌํ˜„ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— findByName() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์œ„์™€ ๊ฐ™์ด ๊ฐ„๋‹จํžˆ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค.



 

JPA  Auditing ์ ์šฉํ•˜๊ธฐ

JPA์—์„œ 'Audit'์ด๋ž€ '๊ฐ์‹œํ•˜๋‹ค'๋ผ๋Š” ๋œป

๊ฐ ๋ฐ์ดํ„ฐ๋งˆ๋‹ค '๋ˆ„๊ฐ€', '์–ธ์ œ' ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ–ˆ๊ณ  ๋ณ€๊ฒฝํ–ˆ๋Š”์ง€ ๊ฐ์‹œํ•œ๋‹ค๋Š” ์˜๋ฏธ


๋Œ€ํ‘œ์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ํ•„๋“œ

  • ์ƒ์„ฑ ์ฃผ์ฒด
  • ์ƒ์„ฑ ์ผ์ž
  • ๋ณ€๊ฒฝ ์ฃผ์ฒด
  • ๋ณ€๊ฒฝ ์ผ์ž

์ด๋Ÿฌํ•œ ํ•„๋“œ๋“ค์€ ๋งค๋ฒˆ ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ ๋˜๋Š” ๋ณ€๊ฒฝ ๋•Œ๋งˆ๋‹ค ๊ฐ’์„ ์ฃผ์ž…ํ•ด์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€. ์ด๋ฅผ ํ•ด์†Œํ•˜๊ธฐ ์œ„ํ•ด Spring Data JPA์—์„œ ์ด๋Ÿฌํ•œ ๊ฐ’์„ ์ž๋™์œผ๋กœ ๋„ฃ์–ด์ฃผ๋Š” ๊ธฐ๋Šฅ ์ œ๊ณต.

 

๐Ÿ“Œ JPA Auditing ๊ธฐ๋Šฅ ํ™œ์„ฑํ™”

1. main() ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” ํด๋ž˜์Šค์— @EnableJpaAuditing ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€

@SpringBootApplication
@EnableJpaAuditing
public class JpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaApplication.class, args);
    }

}

 

OR

2. Configuration ํด๋ž˜์Šค ์ƒ์„ฑ

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfiguration {

}
  • Configuration ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํด๋ž˜์Šค์˜ ๊ธฐ๋Šฅ๊ณผ ๋ถ„๋ฆฌํ•ด์„œ ํ™œ์„ฑํ™”ํ•œ๋‹ค.

 

๐Ÿ“Œ BaseEntity ๋งŒ๋“ค๊ธฐ

์ฝ”๋“œ์˜ ์ค‘๋ณต์„ ์—†์• ๊ธฐ ์œ„ํ•ด ๊ฐ ์—”ํ‹ฐํ‹ฐ์— ๊ณตํ†ต์œผ๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๋Š” ์ปฌ๋Ÿผ(ํ•„๋“œ)์„ ํ•˜๋‚˜์˜ ํด๋ž˜์Šค๋กœ ๋นผ๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

@Getter
@Setter
@ToString
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt; // ์ƒ์„ฑ ์ผ์ž

    @LastModifiedDate
    private LocalDateTime updatedAt; // ๋ณ€๊ฒฝ ์ผ์ž

}
  • @MappedSuperClass : JPA์˜ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๊ฐ€ ์ƒ์†๋ฐ›์„ ๊ฒฝ์šฐ ์ž์‹ ํด๋ž˜์Šค์—๊ฒŒ ๋งคํ•‘ ์ •๋ณด ์ „๋‹ฌ
  • @EntityListeners : ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ์šฉํ•˜๊ธฐ ์ „ํ›„๋กœ ์ฝœ๋ฐฑ์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜
  • AuditingEntityListener : ์—”ํ‹ฐํ‹ฐ์˜ Auditing ์ •๋ณด๋ฅผ ์ฃผ์ž…ํ•˜๋Š” JPA ์—”ํ‹ฐํ‹ฐ ๋ฆฌ์Šค๋„ˆ ํด๋ž˜์Šค
  • @CreatedDate : ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋‚ ์งœ๋ฅผ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜
  • @LastModifiedDate : ๋ฐ์ดํ„ฐ ์ˆ˜์ • ๋‚ ์งœ๋ฅผ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜


์œ„์™€ ๊ฐ™์ด BaseEntity๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Product ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค์—์„œ ๊ณตํ†ต ์ปฌ๋Ÿผ์„ ์ œ๊ฑฐํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค.

@Entity
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Table(name = "product")
public class Product extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long number;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private Integer price;

    @Column(nullable = false)
    private Integer stock;

}

@ToString, @EqualAndHashCode ์–ด๋…ธํ…Œ์ด์…˜์— ์ ์šฉํ•œ callSuper ์†์„ฑ์€ ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ํ•„๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋ฉด ๋งค๋ฒˆ LocalDateTime.now() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์‹œ๊ฐ„์„ ์ฃผ์ž…ํ•˜์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ ๊ฐ’์ด ์ƒ์„ฑ๋œ๋‹ค.

 

๐Ÿ“Œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

    @Test
    public void auditingTest() {
        Product product = new Product();
        product.setName("ํŽœ");
        product.setPrice(1000);
        product.setStock(100);

        Product savedProduct = productRepository.save(product);

        System.out.println("productName : " + savedProduct.getName());
        System.out.println("createdAt : " + savedProduct.getCreatedAt());
    }
// ์ถœ๋ ฅ ๊ฒฐ๊ณผ
productName : ํŽœ
createdAt : 2023-11-20T16:13:13.546600200

 

Product ์—”ํ‹ฐํ‹ฐ์— ์ƒ์„ฑ์ผ์ž๋ฅผ ์ž…๋ ฅํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋Š” ์ƒ์„ฑ์ผ์ž๊ฐ€ ์ €์žฅ๋˜๋ฉฐ, ์—”ํ‹ฐํ‹ฐ์˜ ํ•„๋“œ๋ฅผ ์ถœ๋ ฅํ•ด ๋ณด๋ฉด ํ•ด๋‹น ์‹œ๊ฐ„์ด ์ถœ๋ ฅ๋จ.

 

 

 


QUIZ

1. JPA์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฟผ๋ฆฌ๋ฅผ ( JPQL )์ด๋ผ๊ณ  ํ•œ๋‹ค. 

2. ( find...By )์€/๋Š” ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ์ฃผ์ œ ํ‚ค์›Œ๋“œ์ด๋‹ค. 

3. ( ํŽ˜์ด์ง• )์€/๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ๊ฐœ์ˆ˜๋กœ ๋‚˜๋ˆ  ํŽ˜์ด์ง€๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

4. ( @Query ) ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์ง์ ‘ JPQL์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

5. ( QueryDSL )์€/๋Š” ์ •์  ํƒ€์ž…์„ ์ด์šฉํ•ด SQL๊ณผ ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค. 

6. JPA Auditing ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” main() ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” ํด๋ž˜์Šค์— ( @EnableJpaAuditing ) ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. 

7. ( @CreatedDate )์€/๋Š” ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋‚ ์งœ๋ฅผ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•ด ์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค. 

 

 


PROGRAMMING QUIZ

 

1. Person ์—”ํ‹ฐํ‹ฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ผ. ์ด ๋ฉ”์„œ๋“œ๋Š” age ์†์„ฑ๊ณผ city ์†์„ฑ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด์•ผ ํ•œ๋‹ค.

 

2. Person ์—”ํ‹ฐํ‹ฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ผ. ์ด ๋ฉ”์„œ๋“œ๋Š” firstname ์†์„ฑ๊ณผ phone ์†์„ฑ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด์•ผ ํ•œ๋‹ค. 

 


 

 

1๋ฒˆ ๋‹ต 

List<Person> findByAgeAndCity(int age, String city);

 

 

2๋ฒˆ ๋‹ต

List<Person> findByFirstnameAndPhone(String firstname, String phone);

 

 

 

 


 

์ถœ์ฒ˜: ์žฅ์ •์šฐ, ใ€Ž์Šคํ”„๋ง ๋ถ€ํŠธ ํ•ต์‹ฌ ๊ฐ€์ด๋“œใ€, ์œ„ํ‚ค๋ถ์Šค(2022), p208-245.
Corner Spring 1
Editor:  lyonglyong

728x90

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ