๊ด€๋ฆฌ ๋ฉ”๋‰ด

์‚ถ์˜ ๊ณต์œ 

[Spring]JPQL๊ณผ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ ์‰ฝ๊ฒŒ ๋ฐฐ์šฐ๊ธฐ – SQL ์—†์ด JPA๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ! ๋ณธ๋ฌธ

Web Dev/BackEnd

[Spring]JPQL๊ณผ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ ์‰ฝ๊ฒŒ ๋ฐฐ์šฐ๊ธฐ – SQL ์—†์ด JPA๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ!

dkrehd 2025. 5. 10. 06:31
728x90
๋ฐ˜์‘ํ˜•

๐Ÿง  JPQL๊ณผ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ ์‰ฝ๊ฒŒ ๋ฐฐ์šฐ๊ธฐ – SQL ์—†์ด JPA๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ!

์•ˆ๋…•ํ•˜์„ธ์š”!
์˜ค๋Š˜์€ Spring Data JPA์—์„œ SQL ์—†์ด๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•,
๋ฐ”๋กœ JPQL๊ณผ **๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ(Native Query)**์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”.
์ดˆ๋“ฑํ•™์ƒ๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ์‰ฝ๊ณ  ์žฌ๋ฏธ์žˆ๊ฒŒ ํ’€์–ด๋ณผ๊ฒŒ์š”!


๐Ÿ“Œ 1. JPQL์ด๋ž€?

๐Ÿ—ฃ JPQL = Java Persistence Query Language

JPQL์€ ํ…Œ์ด๋ธ”์ด ์•„๋‹ˆ๋ผ "๊ฐ์ฒด(Entity)"๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•ด์š”.
๋งˆ์น˜ “ํ•™์ƒ(Student) ์ค‘์—์„œ ์ด๋ฆ„์ด ๋ฏผ์ˆ˜์ธ ์• ๋ฅผ ์ฐพ์•„์ค˜”๋ผ๊ณ  ๋งํ•˜๋Š” ๋А๋‚Œ์ด์ฃ !

 

์˜ˆ:

-- SQL 
SELECT * FROM board WHERE title = '์ œ๋ชฉ1'; 

-- JPQL 
SELECT b FROM Board b WHERE b.title = '์ œ๋ชฉ1';
  • Board๋Š” ํ…Œ์ด๋ธ”์ด ์•„๋‹ˆ๋ผ ์ž๋ฐ” ํด๋ž˜์Šค
  • b๋Š” ๋ณ„๋ช…(alias)

 

์ž ๊น! ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“œ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋“ค๋„ ์žˆ๋‚˜์š”?

๋„ค! Spring Data JPA๋Š” ์‚ฌ์‹ค ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค˜์š”.

  • ์ž๋™ ์ƒ์„ฑ:
    • ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ (Query Methods): ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ํŠน์ • ๊ทœ์น™์— ๋งž๊ฒŒ ๋งŒ๋“ค๋ฉด (์˜ˆ: findByTitle(String title)), Spring Data JPA๊ฐ€ ์•Œ์•„์„œ ์ฟผ๋ฆฌ๋ฅผ ๋š๋”ฑ ๋งŒ๋“ค์–ด์ค˜์š”! (๊ฐ€์žฅ ๊ฐ„ํŽธํ•œ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜์ฃ !)
    • JPA Criteria (JPA ํ‘œ์ค€): ์ž๋ฐ” ์ฝ”๋“œ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์กฐ๋ฆฝํ•˜๋Š” ๋ฐฉ์‹์ด์—์š”. (์ข€ ๋” ๋ณต์žกํ•  ์ˆ˜ ์žˆ์–ด์š”)
    • Querydsl (์˜คํ”ˆ ์†Œ์Šค): ์—ญ์‹œ ์ž๋ฐ” ์ฝ”๋“œ๋กœ ์•ˆ์ „ํ•˜๊ณ  ์‰ฝ๊ฒŒ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋„์™€์ฃผ๋Š” ์ธ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ˆ์š”.
  • ์ˆ˜๋™ ์ƒ์„ฑ (์˜ค๋Š˜์˜ ์ฃผ์ธ๊ณต!):
    • EntityManager์˜ createQuery() ์‚ฌ์šฉ: JPA ํ‘œ์ค€ ๋ฐฉ๋ฒ•์œผ๋กœ, ์ง์ ‘ JPQL ๋ฌธ์ž์—ด์„ ์ „๋‹ฌํ•ด์„œ ์…”๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด์š”.
    • @Query ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ: Spring Data JPA์˜ ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ! ์ธํ„ฐํŽ˜์ด์Šค ๋ฉ”์„œ๋“œ ์œ„์— @Query๋ฅผ ๋ถ™์ด๊ณ  JPQL์ด๋‚˜ SQL์„ ์ง์ ‘ ์จ๋„ฃ์„ ์ˆ˜ ์žˆ์–ด์š”.

์˜ค๋Š˜์€ ์ด ์ค‘์—์„œ ์ˆ˜๋™์œผ๋กœ ์šฐ๋ฆฌ๋งŒ์˜ JPQL๊ณผ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ์ง‘์ค‘ํ•ด ๋ณผ๊ฒŒ์š”!


๐Ÿ›  2. JPQL ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•

JPQL์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ 2๊ฐ€์ง€๊ฐ€ ์žˆ์–ด์š”!

โœ… ๋ฐฉ๋ฒ• 1: @Query ์• ๋„ˆํ…Œ์ด์…˜ ์‚ฌ์šฉ

์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  Repository ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฉ”์„œ๋“œ ์œ„์— @Query ๋ผ๋Š” ๋งˆ๋ฒ• ๋”ฑ์ง€๋ฅผ ๋ถ™์ด๊ณ , ๊ทธ ์•ˆ์— JPQL ๋ ˆ์‹œํ”ผ๋ฅผ ์ ์–ด ๋„ฃ๋Š” ๊ฑฐ์˜ˆ์š”. ๋งˆ์น˜ ๋ ˆ์‹œํ”ผ๋ถ์˜ ํŠน์ • ์š”๋ฆฌ๋ฒ• ์œ„์— "ํŠน๋ณ„ ์ง€์‹œ์‚ฌํ•ญ: ์ด๋Œ€๋กœ ์š”๋ฆฌํ•  ๊ฒƒ!" ์ด๋ผ๊ณ  ๋ฉ”๋ชจ๋ฅผ ๋ถ™์ด๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ฃ .

 
// BoardRepository.java ์˜ˆ์‹œ
public interface BoardRepository extends CrudRepository<Board, Long> {
    @Query("SELECT b FROM Board b") // <<-- ๋ฐ”๋กœ ์ด ๋ถ€๋ถ„!
    List<Board> findAllBoard(); // ๋ฉ”์„œ๋“œ ์ด๋ฆ„์€ ์ž์œ ๋กญ๊ฒŒ ์ง€์–ด๋„ ๊ดœ์ฐฎ์•„์š”!
}
  • @Query("์—ฌ๊ธฐ์—_JPQL_๋ ˆ์‹œํ”ผ_์ž‘์„ฑ") ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•ด์š”.
  • ์ค‘์š”! @Query๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, Spring Data JPA๊ฐ€ ์ด JPQL์„ ์‹คํ–‰ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”์„œ๋“œ ์ด๋ฆ„(findAllBoard())์€ ๊ตณ์ด JPA์˜ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ์ƒ์„ฑ ๊ทœ์น™(์˜ˆ: findBy...)์„ ๋”ฐ๋ฅด์ง€ ์•Š์•„๋„ ๊ดœ์ฐฎ์•„์š”! ๊ทธ๋ƒฅ ์šฐ๋ฆฌ๊ฐ€ ์•Œ์•„๋ณด๊ธฐ ์‰ฌ์šด ์ด๋ฆ„์œผ๋กœ ์ง€์œผ๋ฉด ๋œ๋‹ต๋‹ˆ๋‹ค.
  • ์—ฌ๊ธฐ์„œ Board๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ์ด๋ฆ„์ด ์•„๋‹ˆ๋ผ, ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  Board ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค ์ด๋ฆ„์ด๊ณ , b๋Š” Board ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ณ„๋ช…(alias) ๊ฐ™์€ ๊ฑฐ์˜ˆ์š”. JPQL์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ถ€๋ถ„(์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„, ํ•„๋“œ ์ด๋ฆ„ ๋“ฑ)์ด ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•ด์•ผ ํ•ด์š”!

โœ… ๋ฐฉ๋ฒ• 2: EntityManager.createQuery() ์‚ฌ์šฉ

 

๋งˆ์น˜ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์ž(EntityManager)์—๊ฒŒ ์ •์‹์œผ๋กœ "์ด๋Ÿฐ์ด๋Ÿฐ ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์•„์ฃผ์„ธ์š”" ๋ผ๊ณ  ์ƒ์„ธํ•œ ์š”์ฒญ์„œ๋ฅผ ์ž‘์„ฑํ•ด์„œ ๋ณด๋‚ด๋Š” ๊ฒƒ๊ณผ ๊ฐ™์•„์š”.

// BoardRepositoryTest3.java ์˜ˆ์‹œ ์ค‘
// ...
String query = "SELECT b FROM Board b"; // Board ์—”ํ‹ฐํ‹ฐ์—์„œ ๋ชจ๋“  b๋ฅผ ์„ ํƒํ•œ๋‹ค!
TypedQuery<Board> tQuery = em.createQuery(query, Board.class); // EntityManager(em)์—๊ฒŒ ์š”์ฒญ์„œ ์ „๋‹ฌ!
List<Board> list = tQuery.getResultList(); // ๊ฒฐ๊ณผ ๋ชฉ๋ก ๋ฐ›๊ธฐ!
// ...
  • em.createQuery(JPQL๋ฌธ์ž์—ด, ๋ฐ˜ํ™˜๋ _์—”ํ‹ฐํ‹ฐ_ํƒ€์ž….class) ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•ด์š”.
  • JPA์˜ ํ‘œ์ค€ ๋ฐฉ๋ฒ•์ด๋ผ ์–ด๋–ค JPA ๊ตฌํ˜„์ฒด(์˜ˆ: Hibernate)๋ฅผ ์“ฐ๋“  ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•˜์ง€๋งŒ, Spring Data JPA Repository ์ธํ„ฐํŽ˜์ด์Šค์—์„œ๋Š” ๋ณดํ†ต ์•„๋ž˜ ๋ฐฉ๋ฒ•์„ ๋” ๋งŽ์ด ์จ์š”.

๐ŸŽฏ 3. JPQL ๋งค๊ฐœ๋ณ€์ˆ˜ ์ง€์ •ํ•˜๊ธฐ

์ฟผ๋ฆฌ์— ๊ฐ’์„ ๋„ฃ๋Š” ๋ฐฉ๋ฒ•์€ 2๊ฐ€์ง€์˜ˆ์š”!

๋ฐฉ๋ฒ•์˜ˆ์‹œ ์ฝ”๋“œ

 

์ˆœ์„œ๋Œ€๋กœ ?1 b.title = ?1 AND b.writer = ?2
์ด๋ฆ„์œผ๋กœ :๋ณ€์ˆ˜๋ช… b.title = :title AND b.writer = :writer
 
@Query("SELECT b FROM Board b WHERE b.title=?1 AND b.writer=?2")
List<Board> findByTitleAndWriter2(String title, String writer);

@Query("SELECT b FROM Board b WHERE b.title=:title AND b.writer=:writer")
List<Board> findByTitleAndWriter3(String title, String writer);

๐Ÿ 4. ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ(Native Query)๋ž€?

JPQL์ด ์•„๋‹Œ, ์ง„์งœ SQL์„ ๊ทธ๋Œ€๋กœ ์“ฐ๊ณ  ์‹ถ์„ ๋•Œ๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ ์จ์š”!

  • ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ข…๋ฅ˜์— ๋”ฐ๋ผ ์กฐ๊ธˆ์”ฉ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋Š”, ๊ทธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ์˜ ์ˆœ์ˆ˜ํ•œ SQL์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์˜ˆ์š”.
  • ์–ธ์ œ ์‚ฌ์šฉํ• ๊นŒ์š”?
    • JPQL๋กœ ํ‘œํ˜„ํ•˜๊ธฐ ์–ด๋ ค์šด ๋งค์šฐ ๋ณต์žกํ•œ ์ฟผ๋ฆฌ
    • ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ์˜ ๊ณ ์œ  ๊ธฐ๋Šฅ ์‚ฌ์šฉ ์‹œ
    • ํ†ต๊ณ„ ์ฟผ๋ฆฌ๋‚˜ ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๋“ฑ์—์„œ SQL ์ง์ ‘ ํŠœ๋‹์ด ํ•„์š”ํ•  ๋•Œ
  • ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ๊นŒ์š”? @Query ์–ด๋…ธํ…Œ์ด์…˜์— nativeQuery = true ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ผ์š”!
 
// BoardRepository.java ์˜ˆ์‹œ
@Query(value = "SELECT * FROM BOARD", nativeQuery = true) // nativeQuery = true ํ•„์ˆ˜!
List<Board> findAllBoardSQL();โ€‹

 
  • value ์†์„ฑ์—๋Š” ์‹ค์ œ SQL ๋ฌธ์žฅ์„ ์จ์ฃผ๊ณ , nativeQuery = true๋ฅผ ๊ผญ ๋ถ™์—ฌ์ค˜์•ผ "์ด๊ฑด JPQL ์•„๋‹ˆ๊ณ  ์ง„์งœ SQL์ด์•ผ!" ๋ผ๊ณ  Spring Data JPA์—๊ฒŒ ์•Œ๋ ค์ค„ ์ˆ˜ ์žˆ์–ด์š”.
  • ์ฃผ์˜! ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ์ด๋ฆ„๊ณผ ์ปฌ๋Ÿผ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ด์š”. (์˜ˆ: Board ์—”ํ‹ฐํ‹ฐ๊ฐ€ BOARD_TABLE ํ…Œ์ด๋ธ”์— ์ €์žฅ๋œ๋‹ค๋ฉด, SQL์€ SELECT * FROM BOARD_TABLE์ด ๋˜์–ด์•ผ๊ฒ ์ฃ . ์˜ˆ์ œ์—์„œ๋Š” ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„๊ณผ ํ…Œ์ด๋ธ” ์ด๋ฆ„์ด Board๋กœ ๊ฐ™์€ ๊ฒฝ์šฐ์˜ˆ์š”.)

โœ… ์ผ๋ถ€ ์ปฌ๋Ÿผ๋งŒ ์กฐํšŒํ•  ๋•?

 
@Query(value = "SELECT TITLE, WRITER FROM BOARD", nativeQuery = true)
List<Object[]> findAllBoardSQL2();
  • Board ์ „์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ ์ œ๋ชฉ๊ณผ ๊ธ€์“ด์ด๋งŒ ์กฐํšŒํ•ด์š”
  • ๋ฐ˜ํ™˜ ํƒ€์ž…์€ List<Object[]> (๋ฐฐ์—ด ํ˜•ํƒœ)

โ“ ๊ทธ๋Ÿฐ๋ฐ ์™œ Object[]๋กœ ๋ฐ›์•„์•ผ ํ• ๊นŒ์š”?

  • SQL๋กœ SELECT TITLE, WRITER์ฒ˜๋Ÿผ ์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ์„ ๊ณจ๋ผ์„œ ์กฐํšŒํ•˜๋ฉด,
  • JPA๋Š” ์ด ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ํด๋ž˜์Šค(Board)๋กœ ๋งŒ๋“ค ์ˆ˜ ์—†์–ด์š”. ์™œ๋ƒํ•˜๋ฉด content, viewCnt ๊ฐ™์€ ๋‹ค๋ฅธ ์ •๋ณด๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ด์ฃ !

๊ทธ๋ž˜์„œ JPA๋Š” ์ด๋ ‡๊ฒŒ ์ฒ˜๋ฆฌํ•ด์š”:

์กฐํšŒ ๊ฒฐ๊ณผ์ €์žฅ ํ˜•ํƒœ
์ „์ฒด ์—”ํ‹ฐํ‹ฐ List<Board>
์ผ๋ถ€ ์ปฌ๋Ÿผ List<Object[]>
 

์˜ˆ๋ฅผ ๋“ค์–ด,
["title1", "writer1"] ์ด๋Ÿฐ ์‹์œผ๋กœ ๋ฐฐ์—ด(Object[])์— ๋‹ด์•„์„œ ๋ฐ˜ํ™˜ํ•ด์š”.


๐Ÿ” ๋งŒ์•ฝ ์ปฌ๋Ÿผ์ด 1๊ฐœ๋ฟ์ด๋ผ๋ฉด?

@Query(value = "SELECT WRITER FROM BOARD", nativeQuery = true) List<Object> findAllWriters();
  • ์ด์ฒ˜๋Ÿผ ํ•˜๋‚˜์˜ ์ปฌ๋Ÿผ๋งŒ ์กฐํšŒํ•˜๋ฉด Object[] ๋Œ€์‹  **๊ทธ๋ƒฅ Object**๋กœ ๋ฐ›์•„๋„ ๋ผ์š”!
  • ์ด์œ ๋Š” ๊ฐ„๋‹จํ•ด์š”: ๋ฐฐ์—ด์ด ํ•„์š” ์—†์œผ๋‹ˆ๊นŒ์š”!

๐Ÿ“ฆ ์ •๋ฆฌ ์š”์•ฝ

์กฐํšŒ ๋ฐฉ์‹๋ฐ˜ํ™˜ ํƒ€์ž…์ด์œ 
SELECT * List<Board> ๋ชจ๋“  ์†์„ฑ์„ ๊ฐ€์ ธ์˜ค๋ฏ€๋กœ Board์— ์ž๋™ ๋งคํ•‘ ๊ฐ€๋Šฅ
SELECT TITLE, WRITER List<Object[]> ์ผ๋ถ€ ์ปฌ๋Ÿผ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๋ฐ›์Œ
SELECT WRITER List<Object> ์ปฌ๋Ÿผ์ด ํ•˜๋‚˜์ผ ๊ฒฝ์šฐ ๋ฐฐ์—ด์ด ํ•„์š” ์—†์Œ
 

์ด์ฒ˜๋Ÿผ ๋ฐ˜ํ™˜ ํƒ€์ž…์€ JPQL ๋˜๋Š” SQL์—์„œ ์–ด๋–ค ์ปฌ๋Ÿผ์„ ์กฐํšŒํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ๊ฒฐ์ •๋ผ์š”.
ํ˜ผ๋™์„ ํ”ผํ•˜๋ ค๋ฉด ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์•ผ ํ•˜๋Š”์ง€๋„ ํ•จ๊ป˜ ์ƒ๊ฐํ•ด์•ผ ํ•œ๋‹ต๋‹ˆ๋‹ค ๐Ÿ˜Š


๐Ÿ“„ 5. ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ๋„ ๊ฐ€๋Šฅํ•ด์š”!

โœ” ํŽ˜์ด์ง• (Pageable)

 
@Query("SELECT b FROM Board b")
Page<Board> findAllWithPaging(Pageable pageable);

โœ” ์ •๋ ฌ (Sort)

 
Sort sort = Sort.by(Sort.Direction.DESC, "inDate");
List<Board> list = boardRepository.findAll(sort);
  • DESC = ๋‚ด๋ฆผ์ฐจ์ˆœ
  • ASC = ์˜ค๋ฆ„์ฐจ์ˆœ

์‹ค์ „ ์ฝ”๋“œ ์—ฟ๋ณด๊ธฐ (ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์„ค๋ช…) ๐Ÿงช

์ด์ œ ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ๋“ค์ด ์‹ค์ œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ(BoardRepositoryTest3.java)์—์„œ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋˜๋Š”์ง€ ์‚ดํŽด๋ณผ๊ฒŒ์š”!

 
// BoardRepositoryTest3.java
@SpringBootTest // ์Šคํ”„๋ง ๋ถ€ํŠธ ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ!
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) // @Order ์ˆœ์„œ๋Œ€๋กœ ํ…Œ์ŠคํŠธ ์‹คํ–‰!
class BoardRepositoryTest3 {
    @Autowired // EntityManager ์™€ BoardRepository ๋ฅผ ์Šคํ”„๋ง์ด ์ค€๋น„ํ•ด์ค˜!
    public EntityManager em;
    @Autowired
    private BoardRepository boardRepository;

    @BeforeEach // ๊ฐ ํ…Œ์ŠคํŠธ(@Test) ์‹คํ–‰ ์ „์— ํ•ญ์ƒ ๋จผ์ € ์‹คํ–‰๋˜๋Š” ๋ถ€๋ถ„!
    public void testData() {
        // ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด 100๊ฐœ์˜ ๊ฒŒ์‹œ๊ธ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด๋‘ฌ์š”.
        for (int i = 0; i < 100; i++) {
            Board board = new Board();
            board.setBno((long)i);
            board.setTitle("title" + i);
            board.setContent("content" + i);
            board.setWriter("writer" + (i % 5)); // ์ž‘์„ฑ์ž๋Š” 0~4๋ฒˆ์ด ๋ฐ˜๋ณต๋ผ์š”.
            board.setViewCnt((long)(Math.random()*100));
            board.setInDate(new Date());
            board.setUpDate(new Date());
            boardRepository.save(board); // CrudRepository์˜ save๋กœ ์ €์žฅ!
        }
    }

    // 1. @Query๋กœ JPQL ์ž‘์„ฑ ํ…Œ์ŠคํŠธ (๋ชจ๋“  ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ)
    @Test
    @DisplayName("@Query๋กœ JPQL์ž‘์„ฑ ํ…Œ์ŠคํŠธ")
    public void queryAnnoTest() {
        List<Board> list = boardRepository.findAllBoard(); // ์šฐ๋ฆฌ๊ฐ€ @Query๋กœ ๋งŒ๋“  ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ!
        assertTrue(list.size() == 100); // 100๊ฐœ ์ž˜ ์ฐพ์•„์™”๋Š”์ง€ ํ™•์ธ!
    }

    // 2. @Query๋กœ JPQL ์ž‘์„ฑ ํ…Œ์ŠคํŠธ - ๋งค๊ฐœ๋ณ€์ˆ˜ ์ˆœ์„œ ๊ธฐ๋ฐ˜
    @Test
    @DisplayName("@Query๋กœ JPQL์ž‘์„ฑ ํ…Œ์ŠคํŠธ - ๋งค๊ฐœ๋ณ€์ˆ˜ ์ˆœ์„œ")
    public void queryAnnoTest2() {
        // ์ œ๋ชฉ์ด "title1"์ด๊ณ  ์ž‘์„ฑ์ž๊ฐ€ "writer1"์ธ ๊ฒŒ์‹œ๊ธ€ ์ฐพ๊ธฐ! (?1, ?2 ๋ฐฉ์‹)
        List<Board> list = boardRepository.findByTitleAndWriter2("title1", "writer1");
        assertTrue(list.size() == 1); // 1๊ฐœ ์ฐพ์•„์ ธ์•ผ ํ•ด์š”!
    }

    // 3. @Query๋กœ JPQL ์ž‘์„ฑ ํ…Œ์ŠคํŠธ - ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„ ๊ธฐ๋ฐ˜
    @Test
    @DisplayName("@Query๋กœ JPQL์ž‘์„ฑ ํ…Œ์ŠคํŠธ - ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„")
    public void queryAnnoTest3() {
        // ์ œ๋ชฉ์ด "title1"์ด๊ณ  ์ž‘์„ฑ์ž๊ฐ€ "writer1"์ธ ๊ฒŒ์‹œ๊ธ€ ์ฐพ๊ธฐ! (:title, :writer ๋ฐฉ์‹)
        List<Board> list = boardRepository.findByTitleAndWriter3("title1", "writer1");
        assertTrue(list.size() == 1); // ์—ญ์‹œ 1๊ฐœ!
    }

    // 4. @Query๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ(SQL) ์ž‘์„ฑ ํ…Œ์ŠคํŠธ (๋ชจ๋“  ์ปฌ๋Ÿผ ์กฐํšŒ)
    @Test
    @DisplayName("@Query๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ(SQL) ์ž‘์„ฑ ํ…Œ์ŠคํŠธ")
    public void queryAnnoTest4() {
        List<Board> list = boardRepository.findAllBoardSQL(); // ๋„ค์ดํ‹ฐ๋ธŒ SQL ํ˜ธ์ถœ!
        assertTrue(list.size() == 100); // 100๊ฐœ ์ž˜ ์ฐพ์•„์™”๋Š”์ง€ ํ™•์ธ!
    }

    // 5. @Query๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ(SQL) ์ž‘์„ฑ ํ…Œ์ŠคํŠธ (์ผ๋ถ€ ์ปฌ๋Ÿผ๋งŒ ์กฐํšŒ)
    @Test
    @DisplayName("@Query๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ(SQL) ์ž‘์„ฑ ํ…Œ์ŠคํŠธ2")
    public void queryAnnoTest5() {
        List<Object[]> list = boardRepository.findAllBoardSQL2(); // TITLE, WRITER๋งŒ!
        list.stream() // ๋ฆฌ์ŠคํŠธ๋ฅผ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜
            .map(arr -> Arrays.toString(arr)) // ๊ฐ ๋ฐฐ์—ด(Object[])์„ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
            .forEach(System.out::println); // ํ™”๋ฉด์— ์ถœ๋ ฅ
        assertTrue(list.size() == 100); // 100๊ฐœ์˜ [์ œ๋ชฉ, ์ž‘์„ฑ์ž] ์Œ์ด ๋‚˜์™”๋Š”์ง€!
    }

    // 6. EntityManager์˜ createQuery๋กœ JPQL ์ž‘์„ฑ ํ…Œ์ŠคํŠธ
    @Test
    @DisplayName("createQuery๋กœ JPQL์ž‘์„ฑ ํ…Œ์ŠคํŠธ")
    public void createQueryTest() {
        String query = "SELECT b FROM Board b"; // JPQL ๋ฌธ์ž์—ด
        TypedQuery<Board> tQuery = em.createQuery(query, Board.class); // EntityManager๋กœ ์ฟผ๋ฆฌ ์ƒ์„ฑ!
        List<Board> list = tQuery.getResultList(); // ๊ฒฐ๊ณผ ๊ฐ€์ ธ์˜ค๊ธฐ!
        // list.forEach(System.out::println); // ํ•„์š”ํ•˜๋ฉด ์ถœ๋ ฅํ•ด์„œ ํ™•์ธ
        assertTrue(list.size() == 100);
    }
}

๊ฐ ํ…Œ์ŠคํŠธ๋Š” @DisplayName์œผ๋กœ ์–ด๋–ค ํ…Œ์ŠคํŠธ์ธ์ง€ ์ด๋ฆ„์„ ๋ถ™์—ฌ์คฌ๊ณ , @BeforeEach์˜ testData() ๋ฉ”์„œ๋“œ ๋•๋ถ„์— ํ•ญ์ƒ 100๊ฐœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ์ƒํƒœ์—์„œ ์‹œ์ž‘ํ•ด์š”. ๊ทธ๋ž˜์„œ ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ์„œ๋กœ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ณ  ๋…๋ฆฝ์ ์œผ๋กœ ์ž˜ ๋Œ์•„๊ฐ€๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค!


๐Ÿ”‘ ์ •๋ฆฌ ์š”์•ฝํ‘œ

๊ฐœ๋…์„ค๋ช…
JPQL Entity ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” JPA ์ „์šฉ SQL
@Query JPQL ๋˜๋Š” SQL์„ Repository์— ์ง์ ‘ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Œ
Native Query ์‹ค์ œ DB์šฉ SQL์„ ์ง์ ‘ ์ž‘์„ฑ (nativeQuery=true)
createQuery() EntityManager๋ฅผ ํ†ตํ•ด JPQL ์ง์ ‘ ์‹คํ–‰
?1, ?2 ์ˆœ์„œ ๊ธฐ๋ฐ˜ ๋งค๊ฐœ๋ณ€์ˆ˜
:์ด๋ฆ„ ์ด๋ฆ„ ๊ธฐ๋ฐ˜ ๋งค๊ฐœ๋ณ€์ˆ˜ (@Param ์ƒ๋žต ๊ฐ€๋Šฅ)
List<Object[]> ์ผ๋ถ€ ์ปฌ๋Ÿผ ์กฐํšŒํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ˜ํ™˜ ํƒ€์ž…

 

 

๐Ÿ“Œ ๋งˆ๋ฌด๋ฆฌ

์˜ค๋Š˜ ์šฐ๋ฆฌ๋Š” Spring Data JPA์—์„œ ์šฐ๋ฆฌ๋งŒ์˜ ํŠน๋ณ„ํ•œ ๋ฐ์ดํ„ฐ ์กฐํšŒ ๋ ˆ์‹œํ”ผ๋ฅผ ๋งŒ๋“œ๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•, JPQL๊ณผ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด ๋ฐฐ์› ์–ด์š”.

  • JPQL์€ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์ž๋ฐ” ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด์—๊ฒŒ ๋ง์„ ๊ฑฐ๋Š” ๋ฐฉ์‹์ด๋ผ ์ข€ ๋” ๊ฐ์ฒด์ง€ํ–ฅ์ ์ด๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ข…๋ฅ˜๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ์ฝ”๋“œ๋ฅผ ๊ฑฐ์˜ ๊ณ ์น  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์–ด์š”. (๋งˆ์น˜ ํ‘œ์ค€์–ด๋กœ ๋Œ€ํ™”ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ฃ !)
  • ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๊ฒŒ ์ง์ ‘ ๊ทธ๋“ค์˜ ์–ธ์–ด(SQL)๋กœ ๋ง์„ ๊ฑฐ๋Š” ๋ฐฉ์‹์ด๋ผ, ์•„์ฃผ ํŠน๋ณ„ํ•˜๊ฑฐ๋‚˜ ๋ณต์žกํ•œ ์ž‘์—…์„ ํ•  ๋•Œ, ๋˜๋Š” ์„ฑ๋Šฅ์„ ๊ทนํ•œ์œผ๋กœ ๋Œ์–ด์˜ฌ๋ฆฌ๊ณ  ์‹ถ์„ ๋•Œ ์œ ์šฉํ•ด์š”. (๋งˆ์น˜ ํŠน์ • ์ง€์—ญ์˜ ๋ฐฉ์–ธ ์ „๋ฌธ๊ฐ€์ฒ˜๋Ÿผ์š”!)

Spring Data JPA์˜ @Query ์–ด๋…ธํ…Œ์ด์…˜ ๋•๋ถ„์— ์ด ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์•„์ฃผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ฃ . ์ƒํ™ฉ์— ๋งž๊ฒŒ JPQL๊ณผ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ ์ž˜ ์„ ํƒํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์—ฌ๋Ÿฌ๋ถ„๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ž์œ ์ž์žฌ๋กœ ๋Œ€ํ™”ํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ „๋ฌธ๊ฐ€๊ฐ€ ๋  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”! ๐ŸŽ‰

 

 

๋ฐ˜์‘ํ˜•