JPA ์ƒ์†๊ด€๊ณ„ ๋งคํ•‘ ์ „๋žต

2022-10-28

JPA Entity ์ƒ์† ๊ด€๊ณ„ ๋งคํ•‘ ์ „๋žต

๐ŸŒŸ ์ถœ์ฒ˜ : ๊ฐœ๋ฐœ์ž์˜ ๊ธฐ๋ก์Šต๊ด€ - [JPA] ์ƒ์†๊ด€๊ณ„ ๋งคํ•‘ ์ „๋žต(@Inheritance, @DiscriminatorColumn)

๊ณต๋ถ€ํ•˜๊ฒŒ ๋œ ๋ฐฐ๊ฒฝ

  1. Spring ํ”„๋กœ์ ํŠธ์— JPA Entity์— ์ƒ์„ฑ, ์ˆ˜์ • ์‹œ๊ฐ„ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์นผ๋Ÿผ์ด ์กด์žฌ.
  2. ๊ตญ์ œํ™”๋ฅผ ์œ„ํ•ด UTC๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‚ผ์•„ BE์—์„œ ์ฒ˜๋ฆฌํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ์—์„œ ์‹œ๊ฐ„์„ ์š”์ฒญ ์‹œ FE์—์„œ ์ง€์—ญํ™”ํ•˜๊ธฐ ์œ„ํ•ด @PrePersist, @PreUpdate ์–ด๋…ธํ…Œ์ด์…˜์ด ์ ์šฉ๋œ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉ.
  3. ํ”„๋กœ์ ํŠธ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” Entity๊ฐ€ ํ™•๋Œ€๋  ๊ฒฝ์šฐ์—๋„ ๊ธฐ์ค€ ์‹œ๊ฐ„์„ UTC๋กœ ์ผ์ •ํ•˜๊ฒŒ ๊ฐ€์ ธ๊ฐ€๊ธฐ ์œ„ํ•ด์„œ ์œ„ ๋ฉ”์†Œ๋“œ๋ฅผ ์ƒ์†๋ฐ›๋„๋ก BaseEntity๋ฅผ ์„ค๊ณ„
  4. ๊ทธ๋ƒฅ ์ƒ์†ํ•˜๋‹ˆ๊นŒ @Table ์–ด๋…ธํ…Œ์ด์…˜์— ์„ค์ •ํ•œ ํ…Œ์ด๋ธ”์ด ์•„๋‹ˆ๋ผ baseEntity๋ผ๋Š” ์ด๋ฆ„์˜ ํ…Œ์ด๋ธ”์ด ์ƒˆ๋กœ ์ƒ๊ธฐ๋ฉด์„œ DTYPE์ด๋ผ๋Š” ์นผ๋Ÿผ์— ์›๋ž˜ ํ…Œ์ด๋ธ” entity๋ช…์ด ์ž…๋ ฅ ๋จ

DB์˜ super type :arrow_right: sub type๋ฅผ ๋ฌผ๋ฆฌ ๋ชจ๋ธ๋กœ ๊ตฌํ˜„ํ•˜๋Š” 3๊ฐ€์ง€ ๋ฐฉ๋ฒ•

  • Enttiy Diagram
    diagram
  • Entity.class
    1@Entity
    2public class BaseEntity {
    3    @Id
    4    @Column(name = "tuple_id")
    5    private String tupleId;
    6    @Column(name = "create_time")
    7    private Timestamp createTime;
    8    @Column(name = "update_time")
    9    private Timestamp updateTime;
    10
    11    @PrePersist
    12    public void beforeAdd() {
    13        LocalDateTime ldt = LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC);
    14        this.createTime = Timestamp.valueOf(ldt);
    15    }
    16
    17    @PreUpdate
    18    public void beforeUpdate() {
    19        LocalDateTime ldt = LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC);
    20        this.updateTime = Timestamp.valueOf(ldt);
    21    }
    22}
  1. JOINED : ๊ฐ๊ฐ์˜ ํ…Œ์ด๋ธ”๋กœ ์ƒ์„ฑํ•œ ๋’ค ์กฐ์ธํ•˜๋Š” ์ „๋žต
  • implement code
    1@Entity
    2@Inheritance(strategy = InheritanceType.JOINED)
    3@DiscriminatorColumn(name="DTYPE") //default๊ฐ€ DTYPE
    4public class BaseEntity {
    5    @Id
    6    @Column(name = "tuple_id")
    7    private String tupleId;
    8    @Column(name = "create_time")
    9    private Timestamp createTime;
    10    @Column(name = "update_time")
    11    private Timestamp updateTime;
    12
    13    @PrePersist
    14    ..
    15}
  • base_item, app_a, app_b, app_c 4๊ฐœ์˜ ํ…Œ์ด๋ธ”์ด ๊ด€๋ฆฌ๋˜๋ฉฐ base_item ํ…Œ์ด๋ธ”์—๋งŒ tuple_id, create_time, update_time ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋œ๋‹ค. tuple_id ์นผ๋Ÿผ์ด PK์ด์ž FK๊ฐ€ ๋œ๋‹ค.
  • ํ•„์ˆ˜๋กœ ์„ ์–ธํ•ด์•ผ ํ•˜๋Š” @DiscriminatorColumn์œผ๋กœ ์ƒ์„ฑ๋œ DTYPE ์นผ๋Ÿผ์€ sub type ํ…Œ์ด๋ธ” ์ค‘ ์–ด๋–ค ๊ณณ์— ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋Š”์ง€ ๋ช…์‹œํ•˜๋Š” ์—ญํ• ์ด๋ฉฐ, default๋Š” ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค ์ด๋ฆ„์ด ์‚ฌ์šฉ๋œ๋‹ค (appA, appB, appC)
  • app_a ํ…Œ์ด๋ธ”์— ์‹ ๊ทœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ฉด insert query๊ฐ€ ๋‘ ๋ฒˆ ์‹คํ–‰๋˜๋ฉฐ, app_a ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑ ์‹œ๊ฐ„๊ณผ ํ•จ๊ป˜ ์กฐํšŒํ•˜๊ณ ์ž ํ•˜๋ฉด ์กฐ์ธ์ด ํ•„์š”ํ•ด์ง„๋‹ค.
  • DB ๊ด€์ ์—์„œ ๊ฐ€์žฅ ์ •๊ทœํ™”๋œ ๊ตฌ์กฐ์ด๋ฉฐ ๋ฉ”๋ชจ๋ฆฌ ์ธก๋ฉด์—์„œ๋„ ์ž‡์ ์ด ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.
  1. SINGLE_TABLE : ํ†ตํ•ฉ ํ…Œ์ด๋ธ”๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋‹จ์ผ ํ…Œ์ด๋ธ” ์ „๋žต
  • implement code
    1@Entity
    2@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    3//@DiscriminatorColumn default DTYPE์œผ๋กœ ์นผ๋Ÿผ ์ƒ์„ฑํ•ด์คŒ
    4public class BaseEntity {
    5    @Id
    6    @Column(name = "tuple_id")
    7    private String tupleId;
    8    @Column(name = "create_time")
    9    private Timestamp createTime;
    10    @Column(name = "update_time")
    11    private Timestamp updateTime;
    12
    13    @PrePersist
    14    ...
    15}
  • ๊ทœ๋ชจ๋‚˜ ๋ณต์žก๋„๊ฐ€ ํฌ์ง€ ์•Š์€ ์—”ํ‹ฐํ‹ฐ๋“ค์˜ ๊ฒฝ์šฐ ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ์˜ ์นผ๋Ÿผ์„ ํ•œ ํ…Œ์ด๋ธ”์— ๋‹ค ์ €์žฅํ•˜๊ณ  DTYPE์œผ๋กœ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • insert query๋„ ํ•œ ๋ฒˆ์ด๊ณ , ์กฐ์ธ๋„ ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • ์ง„ํ–‰ ์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” appA~appC ์— ๋Œ€ํ•œ ์นผ๋Ÿผ ๋ณ€๊ฒฝ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚˜๋ฉฐ DB๋ฅผ ์›น์—์„œ๋งŒ ์ ‘๊ทผํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•œ ํ…Œ์ด๋ธ”์— ๋ชฐ์•„๋‘๊ณ  DTYPE์œผ๋กœ ๊ตฌ๋ถ„ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ธฐ์—๋Š” ๋ฌด๋ฆฌ๊ฐ€ ์žˆ๋‹ค.
  1. TABLE_PER_CLASS : ๊ตฌํ˜„ ํด๋ž˜์Šค๋งˆ๋‹ค ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๋Š” ์ „๋žต
  • implement code
    1@Entity
    2@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
    3public class BaseEntity {
    4    @Id
    5    @Column(name = "tuple_id")
    6    private String tupleId;
    7    @Column(name = "create_time")
    8    private Timestamp createTime;
    9    @Column(name = "update_time")
    10    private Timestamp updateTime;
    11
    12    @PrePersist
    13    ...
    14}
  • 1๋ฒˆ JOINED ์ „๋žต๊ณผ ์œ ์‚ฌํ•ด๋ณด์ด์ง€๋งŒ super type ์—”ํ‹ฐํ‹ฐ์˜ ์นผ๋Ÿผ๋“ค์„ sub type์˜ ์—”ํ‹ฐํ‹ฐ ๋ชจ๋‘์— ๋™์ผํ•˜๊ฒŒ ์ƒ์„ฑํ•˜์—ฌ app_a, app_b, app_c ์ด 3๊ฐœ์˜ ํ…Œ์ด๋ธ”์„ ๊ด€๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค.
  • super type ์—”ํ‹ฐํ‹ฐ๋กœ ์ ‘๊ทผํ•  ๊ฒฝ์šฐ app_a~app_c ๋ฅผ UNION ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์กฐํšŒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฐ ๊ฒฝ์šฐ ์„ฑ๋Šฅ์ด ์ข‹๋‹ค๊ณ  ํ•  ์ˆ˜๋Š” ์—†๋‹ค.

์–ด๋…ธํ…Œ์ด์…˜ ๋“ฑ ์„ค์ •๊ฐ’ ์ •๋ฆฌ

๐ŸŒŸ ์ถœ์ฒ˜ : victolee - [Spring JPA] ์ƒ์†(JOINED ์ „๋žต์„ ์ค‘์‹ฌ์œผ๋กœ)

์ƒ์† ๊ด€๊ณ„ ๊ด€๋ จ

  • @Inheritance
    • ์–ด๋–ค ์ƒ์† ์ „๋žต์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ธ์ง€ ๋ช…์‹œ
  • @DiscriminatorColumn
    • super type ์—”ํ‹ฐํ‹ฐ์—์„œ ์–ด๋–ค ์นผ๋Ÿผ์„ ์‚ฌ์šฉํ•˜์—ฌ sub type ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ตฌ๋ถ„ํ•  ๊ฒƒ์ธ์ง€ ๋ช…์‹œ
  • @DiscriminatorValue
    • ์œ„์˜ ์นผ๋Ÿผ์—์„œ sub type ์—”ํ‹ฐํ‹ฐ ๋ณ„๋กœ ๊ฐ’์„ ๊ฐ€์ง€๋„๋ก ์ง€์ •
    • DB์—์„œ table column value๋กœ ๋“ค์–ด๊ฐˆ ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— ์˜์–ด/ํ•œ๊ธ€ ๊ตฌ๋ถ„ ์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅ

๊ฐ์ฒด ๋งคํ•‘ ๊ด€๋ จ

  • @MappedSuperclass
    • ๊ฐ์ฒด ์ง€ํ–ฅ์ ์œผ๋กœ๋Š” ์ƒ์† ๊ด€๊ณ„๋ฅผ ์œ ์ง€ํ•˜์ง€๋งŒ ์‹ค์ œ ๊ตฌํ˜„๋  entity์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉ
    • super type ์—”ํ‹ฐํ‹ฐ์—์„œ @Entity ๋Œ€์‹  @MappedSuperclass๋กœ ์„ ์–ธ
      • ์‹ค์ œ๋กœ ๊ตฌํ˜„ ๋  ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ถ”์ƒํด๋ž˜์Šค๋กœ ์„ ์–ธ
      • @ID ๋กœ ์„ ์–ธํ•ด์„œ ์‚ฌ์šฉํ•  id ์นผ๋Ÿผ๋„ ํ•„์š”์—†์Œ
    • ์œ„์˜ ์˜ˆ์ œ์—์„œ BaseEntity์— ์ด ์–ด๋…ธํ…Œ์ด์…˜์„ ์ ์šฉํ•˜๋ฉด BaseEntity์— ๋Œ€ํ•œ ์—”ํ‹ฐํ‹ฐ๋Š” ์ƒ์„ฑ๋˜์ง€ ์•Š๊ณ , TABLE_PER_CLASS ์ „๋žต์„ ์ ์šฉํ•œ ๊ฒƒ๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ BaseEntity์— ์žˆ๋Š” ์นผ๋Ÿผ์„ ๋ชจ๋‘ ์ž์‹ sub type ์—”ํ‹ฐํ‹ฐ์— ํฌํ•จํ•˜์—ฌ ์ƒ์„ฑ
  • @AttributeOverride / @AttributeOverrides
    • @MappedSuperclass๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ํŠน์ • ์นผ๋Ÿผ์„ overrideํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์‚ฌ์šฉ
    1@MappedSuperclass
    2public abstract class BaseEntity{
    3    @Column(name = "create_time")
    4    private Timestamp createTime;
    5    @Column(name = "update_time")
    6    private Timestamp updateTime;
    7}
    8
    9@Entity
    10@AttributeOverride(name="updateTime", column=@Column(name="app_a_update_time"))
    11public class AppA extends BaseEntity{
    12  private String appAVar;
    13}
    14
    15@Entity
    16@AttributeOverrides({
    17  @AttributeOverride(name="createTime", column=@Column(name="app_b_create_time")),
    18  @AttributeOverride(name="updateTime", column=@Column(name="app_b_update_time")),
    19})
    20public class AppB extends BaseEntity{
    21  private String appBVar;
    22}

๊ฒฐ๋ก 

  • ์›๋ž˜ ์ ์šฉํ•˜๋ ค๊ณ  ํ–ˆ๋˜ ๋‚ด์šฉ์€ @MappedSuperclass๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ ์šฉํ•˜์˜€์Œ
  • ๊ธฐ์กด์— QueryDsl๋กœ ์ง์ ‘ ์กฐ์ธํ•˜์—ฌ select ํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ๋˜ ๋ถ€๋ถ„์„ JAVA code ์ƒ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ์ƒ์† ๊ด€๊ณ„์— ์žˆ์Œ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ด€๋ จ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ๋“ค์— JOINED ์ „๋žต์„ ์ ์šฉํ•˜์˜€์Œ

์ฐธ์กฐ

  • Entity์—์„œ๋Š” ์—ฌ๋Ÿฌ ๋ณ€์ˆ˜์— @Id๋ฅผ ์„ ์–ธํ•˜์—ฌ ๋ณตํ•ฉํ‚ค๋กœ์จ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด ๊ฒฝ์šฐ @GeneratedValue๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๊ฐ€ ์—†๋‹ค. ์ง์ ‘ ํ• ๋‹น๋งŒ ๊ฐ€๋Šฅ
    (์ถœ์ฒ˜ : catpark's something new - jpa ๋ณตํ•ฉํ‚ค์—์„œ auto increment)
/end of JPA ์ƒ์†๊ด€๊ณ„ ๋งคํ•‘ ์ „๋žต
CONTENT LISTMERRI๏ผ‡s DEVELOG
[js] Default parameters of function & Spread syntax in object literals
2023-02-09