[JPA] @ManyToOne - PK๊ฐ€ ์•„๋‹Œ ํ•„๋“œ๋ฅผ ์ฐธ์กฐํ•  ๋•Œ (feat.referencedColumnName)

2022. 9. 2. 16:11ใ†Backend/๐ŸŒฟ Spring

๊ฐœ์š”

@ManyToOne ์—ฐ๊ด€๊ด€๊ณ„์—์„œ pk ๊ฐ€ ์•„๋‹Œ ๊ฐ’์„ ์ฐธ์กฐํ•  ๋•Œ ๊ฒช์€ ๋ฌธ์ œ๋ฅผ ๊ธฐ๋กํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด์—๋Š” ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งบ์ง€ ์•Š๊ณ  FK๋ฅผ ๋‹จ์ˆœ String ์œผ๋กœ ์ฐธ์กฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ํ…Œ์ด๋ธ”์„ ๊ฐ„๋‹จํžˆ ์กฐ์ธํ•˜์—ฌ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งบ๊ณ  FK ์˜ ํƒ€์ž…์„ String ์—์„œ Entity๋กœ ๋ณ€๊ฒฝํ–ˆ์„ ๋•, ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ ๋ฐ›์„ ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ์˜ ์›์ธ๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ํ’€์–ด ์„ค๋ช…ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 

 

๋ฌธ์ œ ์ƒํ™ฉ

Hospital ์—”ํ‹ฐํ‹ฐ์—์„œ Address ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐธ์กฐํ•˜๋Š” addressCode ๋ผ๋Š” Foreign Key ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ •ํ™•ํžˆ ๋งํ•˜๋ฉด FK ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ์„ ์–ธํ•œ addressCode ํ•„๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด์ด์—†๊ฒŒ๋„ ํ•ด๋‹น ํ•„๋“œ์˜ ํƒ€์ž…์„ Address ๋กœ ์„ ์–ธํ•˜์ง€ ์•Š๊ณ , String ์œผ๋กœ ์ง€์ •ํ•œ ์‹ค์ˆ˜๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์ฒด์ง€ํ–ฅ์ ์ด์ง€ ๋ชปํ•˜๊ณ , ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ๋งบ์–ด์ง€์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ์ฐธ์กฐ๋งŒ ํ•˜๋Š” ์ƒํƒœ๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Entity
@Table(name = "hospital")
public class Hospital {

    @Id @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "address_code")
    private String addressCode;

}
@Entity
@Table(name = "address")
public class Address implements Serializable {

   private static final long serialVersionUID = 1L;

   @Id
   @Column(name = "id", nullable = false)
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;

   @Column(name = "code")
   private String code;

}

์ด ๊ธ€์„ ์ฝ๋Š” ๋ถ„๋“ค์€Hospital ์—”ํ‹ฐํ‹ฐ์˜ addressCode ์˜ ํƒ€์ž…์ด ์–ด์ƒ‰ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋‹จ๋ฒˆ์— ์•Œ์•„์ฐจ๋ฆฌ์‹ค ๊ฒ๋‹ˆ๋‹ค. OOP๋‹ต๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ ค๋ฉด String -> Address ํƒ€์ž…์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

 

ํ•ด๋‹น ๋ถ€๋ถ„์„ ๋ณ€๊ฒฝ ํ–ˆ์Šต๋‹ˆ๋‹ค.

@Entity
@Table(name = "hospital")
public class Hospital {

    @Id @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;


    // ๋ณ€๊ฒฝ ์ „
//    @Column(name = "address_code")
//    private String addressCode;

    
    // ๋ณ€๊ฒฝ ํ›„ 
    @ManyToOne
    @JoinColumn(name = "address_code")
    private Addresss address;

}

 

์ˆ˜์ •ํ•œ ๋‚ด์šฉ์„ ๋ฐ˜์˜ํ•˜์—ฌ QueryDSL JPA๋„ ์‹คํ–‰ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ๋Š” hospital ์ด address ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ, address ์—”ํ‹ฐํ‹ฐ์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

public Page<HospitalListDto> getHospitals(Pageable pageable) {
        JPQLQuery<HospitalListDto> limit = getQuerydsl().createQuery()
                .select(home)
                .from(hospital)
                .leftJoin(address).on(hospital.address.code.eq(address.code))
                .groupBy(hospital.id)
                .limit(pageable.getPageSize());
                
		... 

    }

leftjoin ์ฟผ๋ฆฌ์—์„œ hospital์˜ address ํ•„๋“œ๊ฐ€ address์˜ code ํ•„๋“œ๋ฅผ ์ฐธ์กฐํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ด๋ธ” ์ƒ์—์„  ์•„๋ž˜์™€ ๊ฐ™์€ ๋ชจ์Šต์ด์ฃ .

ํ…Œ์ด๋ธ”์˜ address_code ๋ผ๋Š” ์ปฌ๋Ÿผ๋ช…์ด, Java ์ฝ”๋“œ์—์„  ์•„๋ž˜์ฒ˜๋Ÿผ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.

@Column(name = "address_code")
private Addresss address;

 

QueryDSL ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰ ๋œ ํ›„ ์ •์ƒ์ ์œผ๋กœ ๊ฐ’์ด ๋ฐ˜ํ™˜ ๋  ๊ฒƒ์ด๋ผ ๊ธฐ๋Œ€ํ–ˆ์ง€๋งŒ, ์•„๋ฌด๋Ÿฐ ๊ฐ’๋„ ๋‚˜์˜ค์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

์›์ธ ํŒŒ์•…์„ ์œ„ํ•ด ์‹ค์ œ sql ์ฟผ๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋‚˜๊ฐ”๋Š”์ง€ p6spy ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

 

select distinct hospital0_.id,
                hospital0_.address_code

from core_hospital hospital0_
         left outer join address address2_ on hospital0_.address_code = address2_.id
         left outer join address address1_ on (address1_.code = address2_.code)

group by hospital0_.id
limit 20;

๋ถ„๋ช… QueryDSL ์ฝ”๋“œ์—์„  left Join ์„ ํ•œ๋ฒˆ๋งŒ ๊ฑธ์—ˆ๋Š”๋ฐ, ์‹ค์ œ๋กœ ๋‚˜๊ฐ„ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ฉด left join ์ด ๋‘๋ฒˆ ๊ฑธ๋ ค์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌด์—‡์ด ๋ฌธ์ œ์ผ๊ฐ€์š”? 

@newsroom season1

 

์›์ธ ํŒŒ์•…

์šฐ์„  ๋‘๋ฒˆ๋‚˜๊ฐ„ left join ์ฟผ๋ฆฌ๊ฐ€ ์–ด๋–ค ํ˜•ํƒœ์ธ์ง€ ํ™•์ธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

left outer join address address2_ on hospital0_.address_code = address2_.id -- (1)
left outer join address address1_ on (address1_.code = address2_.code) -- (2)

 

1) hospital ํ…Œ์ด๋ธ”์˜ address_code  ์ปฌ๋Ÿผ์ด address ํ…Œ์ด๋ธ”์˜ id ์™€ ์กฐ์ธํ•ฉ๋‹ˆ๋‹ค. queryDSL ์ฝ”๋“œ๋Œ€๋กœ๋ผ๋ฉด address_code ๋Š” address์˜ code ์ปฌ๋Ÿผ๊ณผ ์กฐ์ธํ•ด์•ผํ•˜๋Š”๋ฐ ๋ง์ด์ฃ . 

 

2) ๋‘๋ฒˆ์งธ ์กฐ์ธ๋ฌธ์—์„ , address ํ…Œ์ด๋ธ”์˜ code ์ปฌ๋Ÿผ์ด, (1) ์—์„œ ์กฐ์ธํ•  ๋•Œ ์‚ฌ์šฉ๋œ address ํ…Œ์ด๋ธ”์˜ code ์ปฌ๋Ÿผ๊ณผ ์กฐ์ธ๋ฉ๋‹ˆ๋‹ค. ๋„ํ†ต ๋ฌด์Šจ ์†Œ๋ฆฌ์ธ์ง€, ์–ด๋–ป๊ฒŒ ํ•ด์„ํ•ด์•ผํ• ์ง€ ๊ฐ์ด ์˜ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

๋ฌธ์ œ ์›์ธ

๊ณต์‹๋ฌธ์„œ์™€ ๊ตฌ๊ธ€๋ง์„ ํ†ตํ•ด ์™œ ์œ„์™€ ๊ฐ™์€ ์ด์ƒํ•œ join ๋ฌธ์ด ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” JoinColumn ์–ด๋…ธํ…Œ์ด์…˜์˜ ์˜ต์…˜์ธ referencedColumnName ์— ๊ด€ํ•œ ์„ค๋ช…์ž…๋‹ˆ๋‹ค. 

@JoinColumn ์–ด๋…ธํ…Œ์ด์…˜ ๊ณต์‹๋ฌธ์„œ

ํ•˜์ด๋ผ์ดํŒ…๋œ ๋ถ€๋ถ„์„ ์˜์—ญํ•ด๋ณด๋ฉด, @JoinColumn(name="value")์˜ value๋Š” ์ฐธ์กฐ ํ…Œ์ด๋ธ”์˜ primary key ์™€ ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ๋””ํดํŠธ ์ง€์ •๋œ๋‹ค๋Š” ๊ฒƒ์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ value๋Š” hospital ์˜ address_code ์— ํ•ด๋‹น๋˜๋Š” ๊ฐ’ ์ž…๋‹ˆ๋‹ค. 

์ด์ฏค์—์„œ address์™€ hospital ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค์‹œ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@Entity
@Table(name = "address")
public class Address implements Serializable {

   private static final long serialVersionUID = 1L;

   @Id
   @Column(name = "id", nullable = false)
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;

   @Column(name = "code")
   private String code;

}
@Entity
@Table(name = "hospital")
public class Hospital {

    @Id @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;

    @ManyToOne
    @JoinColumn(name = "address_code")
    private Addresss address;

}

๋ˆˆ์น˜ ์ฑ„์…จ๋‚˜์š”?

2๋ฒˆ์ด๋‚˜ ์ถœ๋ ฅ๋œ left join ๋ฌธ์ด, referencedColumnName ์˜ default ์†์„ฑ๊ณผ ์—ฐ๊ด€๋ผ ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

referencedColumnName์„ ๋ณ„๋„๋กœ ์ง€์ •ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๋””ํดํŠธ๋กœ pk ๊ฐ’์ธ address.id ๋ฅผ ์ฐธ์กฐํ•œ ๊ฒƒ์ž„์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— queryDSL ์ƒ์˜ ์กฐ์ธ๋ฌธ๊ณผ ์‹ค์ œ query ์˜ ์กฐ์ธ๋ฌธ์ด ์ฐจ์ด๋ฅผ ๊ฐ€์ง„ ๊ฒƒ์ด์ฃ . queryDSL ์—์„œ hospital์˜ fk ๋Š” address ์˜ code ๋ฅผ ์ฐธ์กฐํ•˜๋ ค๊ณ  ํ•˜์ง€๋งŒ,  @JoinColumn ์˜ ๋””ํดํŠธ ์˜ต์…˜์ด id(pk) ๋ฅผ ์ฐธ์กฐํ•ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ์‹ค์ œ ์ฟผ๋ฆฌ (1) ์—์„œ address.id ๊ฐ€ ๋“ฑ์žฅํ•˜๋Š” ์ด์œ ๋ฅผ ์ด์ œ๋Š” ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ฌธ์ œ ํ•ด๊ฒฐ

@Entity
@Table(name = "hospital")
public class Hospital {

    @Id @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;

    @ManyToOne
    @JoinColumn(name = "address_code", referencedColumnName = "code")
    private Address address;
}

์ด์ œ @JoinColumn ์˜ ์˜ต์…˜ referencedColumnName ์— ์šฐ๋ฆฌ๊ฐ€ ์‹ค์ œ๋กœ ์ฐธ์กฐํ•˜๊ณ ์ž ํ•˜๋Š” ์ปฌ๋Ÿผ๋ช… code ์„ ์ ์Šต๋‹ˆ๋‹ค. queryDSL ์„ ๋‹ค์‹œ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

 

์ •๋ฆฌ

  1. ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘ ์‹œ ์ฐธ์กฐํ•˜๋Š” ์ปฌ๋Ÿผ์ด pk ์ธ์ง€ ์•„๋‹Œ์ง€ ํ™•์ธ ํ•ด์•ผํ•œ๋‹ค.
  2. ์ฐธ์กฐํ•˜๋Š” ์ปฌ๋Ÿผ์ด pk ๋ผ๋ฉด @JoinColumn ์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ, ๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒฝ์šฐ referencedColumnName ์— ์ฐธ์กฐํ•˜๋ ค๋Š” column ๋ช…์„ ๋„˜๊ฒจ์ค˜์•ผ ํ•œ๋‹ค. 

 

๊ฒฐ๋ก ๋งŒ ๋†“๊ณ  ๋ณด๋ฉด, @JoinColumn ์˜ referencedColumnName ์„ ์„ค์ •ํ•˜๋ฉด ๋˜๋Š” ๊ฐ„๋‹จํ•œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

๋งž์Šต๋‹ˆ๋‹ค, ๊ฐ„๋‹จํ•œ ๋ฌธ์ œ์ง€๋งŒ ๊ตฌ์ ˆ๊ตฌ์ ˆ ํ’€์–ด์“ด ์…ˆ์ด์ฃ . ์œ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ณผ์ •์—์„œ JPA ์˜ ์—ฐ๊ด€๊ด€๊ณ„๋‚˜ ์–ด๋…ธํ…Œ์ด์…˜ ๋“ฑ์˜ ์ง€์‹์ด ๋ถ€์กฑํ•จ์„ ๋Š๊ผˆ์Šต๋‹ˆ๋‹ค. @JoinColumn ์ด ๋””ํดํŠธ๋กœ ์ฐธ์กฐํ•˜๋Š” ๊ฐ’์ด pk ๋ผ๋Š” ์ง€์‹์ด ๋จธ๋ฆฌ์†์— ์ •๋ฆฌ ๋ผ ์žˆ์—ˆ๋‹ค๋ฉด, ๋” ์ œ๋Œ€๋กœ ๋œ ๊ตฌ๊ธ€๋ง์„ ํ†ตํ•ด ๋ฌธ์ œ๋ฅผ ๋นจ๋ฆฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์•˜์„๊นŒ ์ƒ๊ฐ์ด ๋“ญ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ๋‹ค์‹œ JPA ๊ธฐ๋ณธ์„ ๊ณต๋ถ€ํ•˜๋Ÿฌ 20000 . . .