Spring data jpa

๊ฐœ์š”

  • Spring Data JPA๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œ ํ•˜๋Š” ๊ณผ์ •, ๋˜๋Š” ์—…๊ทธ๋ ˆ์ด๋“œ ํ•œ ์ดํ›„์— ๋ฐœ๊ฒฌ๋œ ์ด์Šˆ์™€ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ์„ ์ •๋ฆฌํ•œ๋‹ค.

์ข…์†์„ฑ ๋ถ„์„

  • Jakarta Persistence 3.1 (PDF) / Jakarta EE Platform 10

  • Spring Boot 3.0.1 (org.springframework.boot:spring-boot:3.0.1)

    • Spring Boot Starter Data JPA 3.0.1 (org.springframework.boot:spring-boot-starter-data-jpa:3.0.1)

      • Spring Data JPA 3.0.0 (org.springframework.data:spring-data-jpa:3.0.0)

        • Spring Data 2022.0.0

        • Jakarta Annotation API 2.1.1 (jakarta.annotation:jakarta.annotation-api:2.1.1)

        • Spring ORM 6.0.3 (org.springframework:spring-orm:6.0.3)

      • Hibernate 6.1.6 (org.hibernate.orm:hibernate-core:6.1.6.Final)

        • Jakarta Persistence API 3.1.0 (jakarta.persistence:jakarta.persistence-api:3.1.0)

        • Jakarta Transaction API 2.0.1 (jakarta.transaction:jakarta.transaction-api:2.0.1)

Hibernate 6์—์„œ ๋Œ€์ฒด ๋ญ๊ฐ€ ๋‹ฌ๋ผ์กŒ๊ธธ๋ž˜?

@OneToOne ๋งคํ•‘ ์ปฌ๋Ÿผ์— UNIQUE ์ œ์•ฝ ์กฐ๊ฑด ์ž๋™ ์ƒ์„ฑ

  • ์ด์ „ ๋ฒ„์ „์—์„œ๋Š” @OneToOne ๋งคํ•‘๋œ ์ปฌ๋Ÿผ์— ๋Œ€ํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค UNIQUE ์ œ์•ฝ ์กฐ๊ฑด์„ ๋งŒ๋“ค์ง€ ์•Š์•˜์ง€๋งŒ, 6.2 ๋ฒ„์ „๋ถ€ํ„ฐ๋Š” UNIQUE ์ œ์•ฝ ์กฐ๊ฑด์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. (Hibernate DDL ์Šคํ‚ค๋งˆ ์ž๋™ ์ƒ์„ฑ ๊ธฐ๋Šฅ์œผ๋กœ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ์— ํ•ด๋‹น)

    • ๋งŒ์•ฝ ๋งคํ•‘๋œ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•ด ๋…ผ๋ฆฌ์  Soft-delete ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉ์ค‘์ด๋ผ๋ฉด (ex. is_used, deleted ๋“ฑ์˜ ๋น„ํ™œ์„ฑํ™”/์‚ญ์ œ ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ) ํ•ด๋‹น ์ปฌ๋Ÿผ์— ์ค‘๋ณต ๊ฐ’์ด ์กด์žฌํ•  ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Hibernate DDL ์ž๋™ ์ƒ์„ฑ์„ ์ด์šฉํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ @jakarta.persistence.ForeignKey(NO_CONSTRAINT)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ UNIQUE ์ œ์•ฝ ์กฐ๊ฑด ์ƒ์„ฑ์„ ๊ฑด๋„ˆ ๋›ธ ์ˆ˜ ์žˆ๋‹ค.

  • ๋˜๋Š”, ๊ธฐ์กด์˜ @OneToOne ๋งคํ•‘์„ @ManyToOne + @UniqueConstraint๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

SQL์„ ์ƒ์„ฑํ•˜๋Š” ๋‚ด๋ถ€ ๋ฐฉ์‹์ด ๋‹ฌ๋ผ์กŒ์–ด์š”

  • Hibernate 5๊นŒ์ง€๋Š” Criteria API๊ฐ€ ๋จผ์ € JPQL/HQL๋กœ ๋ณ€ํ™˜๋œ ํ›„, ์ด๋กœ๋ถ€ํ„ฐ SQL์„ ์ง์ ‘ ์ƒ์„ฑํ•˜๋Š” ๊ตฌ์กฐ์˜€๋‹ค.

  • Hibernate 6๋ถ€ํ„ฐ๋Š” SQL์„ AST(Abstract Syntax Tree)ํ˜•ํƒœ๋กœ ์ถ”์ƒํ™”ํ•œ SQM(Semantic Query Model)์ด ๋„์ž…๋˜์—ˆ๋‹ค. Criteria API๋“  JPQL/HQL์ด๋“  ๋จผ์ € SQM์œผ๋กœ ๋ณ€ํ™˜๋˜๊ณ , SQM ๋ชจ๋ธ์€ ์ผ์ •ํ•œ ๊ทœ์น™์— ๋”ฐ๋ผ SQL ๊ตฌ๋ฌธ์œผ๋กœ ๋ณ€ํ™˜๋œ๋‹ค.

  • org.hibernate.orm.query.sqm.ast ๋กœ๊ฑฐ์˜ ๋กœ๊น… ๋ ˆ๋ฒจ์„ debug๋กœ ๋ฐ”๊พธ๋ฉด SQL AST๊ฐ€ ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค์–ด์ง€๋Š”์ง€ ๋กœ๊ทธ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ํ•˜์ง€๋งŒ SQM์˜ ์‹ ๊ทœ ๋„์ž…์œผ๋กœ ์ธํ•ด SQM์„ ๋งŒ๋“œ๋Š” ๊ณผ์ •, ๋˜๋Š” SQM์„ SQL๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ด ์•„์ง ์•ˆ์ •ํ™”๋˜์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค. ์ด์— ๋”ฐ๋ผ ์ƒ์„ฑ๋œ SQL์ด ๊ธฐ๋Œ€ํ•œ SQL๊ณผ ๋‹ค๋ฅธ ์—ฌ๋Ÿฌ ์ผ€์ด์Šค๊ฐ€ ๋ฐœ๊ฒฌ๋˜๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์ด๋‹ค.

  • ํ•œ ์ค„ ์š”์•ฝ: Hibernate 6๋กœ ์˜ค๋ฉด์„œ SQL์„ ์ƒ์„ฑํ•˜๋Š” ๋‚ด๋ถ€ ๋กœ์ง์ด ์™„์ „ํžˆ ๋ณ€๊ฒฝ๋˜์—ˆ๊ณ , ์•„์ง๊นŒ์ง€ ์•ˆ์ •ํ™” ๋˜์ง€ ์•Š์•„ ์—ฌ๋Ÿฌ ๋ฒ„๊ทธ๊ฐ€ ์กด์žฌํ•œ๋‹ค.

์ด์Šˆ

  • ๋งŒ์•ฝ Hibernate์˜ ๋ฒ„๊ทธ ๋˜๋Š” ์ด์Šˆ๋ผ๊ณ  ์ƒ๊ฐ๋˜๋Š” ๊ฒฝ์šฐ Hibernate ORM Issue Board์—์„œ ๋น„์Šทํ•œ ์ด์Šˆ๊ฐ€ ๋ฆฌํฌํŠธ ๋œ ๊ฒƒ์ด ์žˆ๋Š”์ง€๋ถ€ํ„ฐ ํ™•์ธํ•ด๋ณด์ž.

@Where๋ฅผ ์‚ฌ์šฉํ•œ ์กฐ์ธ ์ฟผ๋ฆฌ ์กฐ๊ฑด์ด ์›ํ•˜๋Š” ๋Œ€๋กœ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š์Œ

  • ๋น„์Šทํ•œ ์ด์Šˆ: @OneToMany relationship with @Where on child table generates wrong sql (HHH-15902)

    • 2022-12-17 ๋ฆฌํฌํŠธ ๋œ ์ดํ›„ ์•„์ง ํ•ด๊ฒฐ๋˜์ง€ ์•Š์€ ์ด์Šˆ

  • ๋งŒ๋“ค์–ด์ง€๋Š” SQL๋ฌธ์˜ ์กฐ๊ฑด์ ˆ ์œ„์น˜๊ฐ€ ๋‹ฌ๋ผ์ ธ์„œ, ์›ํ•˜๋˜ LEFT OUTER JOIN์ด ์•„๋‹Œ INNER JOIN์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๊ฒŒ ๋˜์–ด ๋ฒ„๋ฆผ

๋ฐ๋ชจ ํ”„๋กœ์ ํŠธ

  • ์ •๋ง Hibernate 6.1์˜ ๋ฌธ์ œ์ธ ๊ฒƒ์ธ์ง€ ํ™•์ธํ•ด ๋ณด๊ธฐ ์œ„ํ•ด์„œ, ๊ธฐํƒ€ ๋‹ค๋ฅธ ์ข…์†์„ฑ ์—†์ด Hibernate Core๋งŒ ๊ฐ€์ง€๊ณ  ๋ฐ๋ชจ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐœ๋ฐœํ•˜์—ฌ ํ…Œ์ŠคํŠธ ํ•ด ๋ณด์•˜๋‹ค.

select
    member0_.id as id1_1_0_,
    member0_.name as name2_1_0_,
    memberinfo1_.id as id1_0_1_,
    memberinfo1_.active as active2_0_1_,
    memberinfo1_.city as city3_0_1_,
    memberinfo1_.member_id as member_i4_0_1_ 
from
    members member0_ 
left outer join
    member_information memberinfo1_ 
        on member0_.id=memberinfo1_.member_id 
        and (
            memberinfo1_.active = 1
        )  
where
    member0_.id=?
select
    m1_0.id,
    i1_0.id,
    i1_0.active,
    i1_0.city,
    i1_0.member_id,
    m1_0.name 
from
    members m1_0 
left join
    member_information i1_0 
        on m1_0.id=i1_0.member_id 
where
    m1_0.id=?
  • ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•: docker-compose ์ด์šฉํ•˜์—ฌ ๋กœ์ปฌ MySQL 5.7 ๋„์›Œ๋‘” ํ›„ Application.kt์˜ main() ํ•จ์ˆ˜ ์‹คํ–‰

  • ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ: Hibernate 5.6 โ†’ 6.1 ์—…๊ทธ๋ ˆ์ด๋“œ ์ดํ›„ Fetch Join ๋ฐฉ์‹๊ณผ Entity Graph ๋ฐฉ์‹ ์กฐํšŒ์‹œ ๋งŒ๋“ค์–ด์ง€๋Š” SQL ๊ตฌ๋ฌธ์ด ๋‹ฌ๋ผ์กŒ๋‹ค๋Š” ๊ฒƒ, ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋กœ ์ธํ•ด ์กฐํšŒ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์กŒ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

    • ๊ตฌ์ฒด์ ์œผ๋กœ๋Š”, Entity Graph๋ฅผ ์ด์šฉํ•ด์„œ @OneToOne ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ fetch Join ํ•˜๋Š” ๊ฒฝ์šฐ์— @Entity ํด๋ž˜์Šค์— ๋ถ™์ธ @Where clause๊ฐ€ ๋ฌด์‹œ๋˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.

@DiscriminatorValue๋ฅผ ์‚ฌ์šฉํ•œ ์—”ํ‹ฐํ‹ฐ์˜ ์กฐ์ธ ์ฟผ๋ฆฌ๊ฐ€ ์ž˜๋ชป ๋งŒ๋“ค์–ด์ง

SELECT *
  FROM users user0_
  LEFT OUTER JOIN udf_value properties14_ ON user0_.id = properties14_.entity_id
      AND properties14_.target_type = 1
  WHERE user0_.id = ?
 SELECT *
  FROM users u1_0
  LEFT JOIN udf_value p2_0 ON u1_0.id = p2_0.entity_id
  WHERE p2_0.target_type = 1
      AND u1_0.id = ?
  • ์กฐ์ธ์˜ ON ์ ˆ์— ๋“ค์–ด๊ฐ€๋˜ ์กฐ๊ฑด์ด ๋ฐ”๊นฅ WHERE ๋ฌธ์œผ๋กœ ๋น ์ ธ๋‚˜์˜จ๋‹ค๋Š” ์ ์—์„œ @Where๋ฅผ ์‚ฌ์šฉํ•œ ์ฟผ๋ฆฌ๊ฐ€ ์ž˜๋ชป ๋งŒ๋“ค์–ด์ง€๋Š” ์ด์Šˆ์™€ ๋งค์šฐ ์œ ์‚ฌํ•˜๋‹ค. (ํ•จ๊ป˜ fix๋  ๊ฐ€๋Šฅ์„ฑ๋„ ์žˆ์„ ์ˆ˜ ์žˆ๊ฒ ๋‹ค.)

Useful Articles

Last updated