객체와 ν…Œμ΄λΈ” 맀핑


JPA μ—μ„œ 제일 μ€‘μš”ν•œ 2가지

  1. μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ 및 λ‚΄λΆ€ λ™μž‘ 방식
  2. 객체와 μ—”ν‹°ν‹°μ˜ 맀핑 섀계

μ—”ν‹°ν‹° 맀핑 μ†Œκ°œ

JPA λŠ” 객체와 μ—”ν‹°ν‹°λ₯Ό λ§€ν•‘ν•˜κΈ° μœ„ν•΄ μ•„λž˜μ™€ 같은 λ‹€μ–‘ν•œ μ• λ„ˆν…Œμ΄μ…˜μ„ μ§€μ›ν•œλ‹€.

  • @Entity @Table 객체와 ν…Œμ΄λΈ” 맀핑을 μœ„ν•œ μ• λ„ˆν…Œμ΄μ…˜
  • @Column ν•„λ“œμ™€ 컬럼 맀핑을 μœ„ν•œ μ• λ„ˆν…Œμ΄μ…˜
  • @Id κΈ°λ³Έ ν‚€ 맀핑을 μœ„ν•œ μ• λ„ˆν…Œμ΄μ…˜
  • @ManyToOne @JoinColumn 연관관계 맀핑을 μœ„ν•œ μ• λ„ˆν…Œμ΄μ…˜ 이번 κ°•μ˜μ—μ„œλŠ” 연관관계 맀핑을 μ œμ™Έν•œ λͺ¨λ“  μ• λ„ˆν…Œμ΄μ…˜μ„ 닀뀄볼 것이닀.

@Entity

@Entity κ°€ 뢙은 ν΄λž˜μŠ€λŠ” JPA κ°€ κ΄€λ¦¬ν•œλ‹€. λ•Œλ¬Έμ—, JPA λ₯Ό μ‚¬μš©ν•΄μ„œ ν…Œμ΄λΈ”κ³Ό 맀핑할 ν΄λž˜μŠ€λŠ” @Entity κ°€ ν•„μˆ˜μ μ΄λ‹€. @Entity μ‚¬μš© μ‹œ μ£Όμ˜ν•  점이 λͺ‡κ°€μ§€ μžˆλŠ”λ°, μ΄λŠ” μ•„λž˜μ™€ κ°™λ‹€.

  • 동적 생성을 μœ„ν•œ public λ˜λŠ” protected κΈ°λ³Έ μƒμ„±μžκ°€ ν•„μˆ˜μ μ΄λ‹€.
  • final 클래슀, enum, interface, inner ν΄λž˜μŠ€λŠ” μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€.
  • μ €μž₯ν•  ν•„λ“œμ— final 을 μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€. name 속성을 톡해 @Entity(name = "Member") 처럼 μ‚¬μš©ν•  μ—”ν‹°ν‹°μ˜ 이름을 지정할 수 μžˆλ‹€.
  • κΈ°λ³Έκ°’μœΌλ‘œ 클래슀 이름을 μ‚¬μš©ν•œλ‹€.
  • λ‹€λ₯Έ νŒ¨ν‚€μ§€μ— 이름이 같은 ν΄λž˜μŠ€κ°€ μ—†μœΌλ©΄ 가급적 기본값을 μ‚¬μš©ν•œλ‹€.

@Table

@Table 은 엔티티와 맀핑할 ν…Œμ΄λΈ” μ§€μ •ν•˜λŠ” μ• λ„ˆν…Œμ΄μ…˜μ΄λ‹€. @Table μ• λ„ˆν…Œμ΄μ…˜μ—λŠ” λ‹€μ–‘ν•œ 속성이 μ‘΄μž¬ν•˜λŠ”λ°, μ΄λŠ” μ•„λž˜μ™€ κ°™λ‹€.

  • name μ†μ„±μœΌλ‘œ DB ν…Œμ΄λΈ” 이름이 MBR 일 경우 @Table(name = "MBR") 둜 지정할 수 μžˆλ‹€.
  • calalog μ†μ„±μœΌλ‘œ λ°μ΄ν„°λ² μ΄μŠ€ catalog 맀핑할 수 μžˆλ‹€.
  • schema μ†μ„±μœΌλ‘œ λ°μ΄ν„°λ² μ΄μŠ€ schema 맀핑할 수 μžˆλ‹€.
  • uniqueConstraints μ†μ„±μœΌλ‘œ DDL 생성 μ‹œ μœ λ‹ˆν¬ μ œμ•½ 쑰건을 생성할 수 μžˆλ‹€.

λ°μ΄ν„°λ² μ΄μŠ€ μŠ€ν‚€λ§ˆ μžλ™ 생성


JPA λŠ” 맀핑 μ •λ³΄λ§Œ 보고 무슨 ν…Œμ΄λΈ”μ΄ ν•„μš”ν•œμ§€ μ•ŒκΈ° λ•Œλ¬Έμ— DDL 을 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ μ‹œμ μ— μžλ™ μƒμ„±ν•˜μ—¬ ν…Œμ΄λΈ”μ„ μžλ™μœΌλ‘œ 생성해쀀닀. μ΄λŠ” μ—¬λŸ¬κ°€μ§€ μž₯점을 μ œκ³΅ν•˜λŠ”λ°, μ΄λŠ” μ•„λž˜μ™€ κ°™λ‹€.

  • ν…Œμ΄λΈ” 쀑심 κ°œλ°œμ—μ„œ 객체 쀑심 개발둜 μ „ν™˜ν•  수 μžˆλ‹€.
  • DB 방언을 ν™œμš©ν•΄μ„œ DB 에 λ§žλŠ” μ μ ˆν•œ DDL 을 μƒμ„±ν•œλ‹€. 이런 μžλ™ 생성 DDL 은 운영 μ„œλ²„μ—μ„œλŠ” λΆˆμ•ˆν•  수 있기 λ•Œλ¬Έμ— μ•„λž˜μ™€ 같은 주의 사항이 μ‘΄μž¬ν•œλ‹€.
  • μ΄λ ‡κ²Œ μƒμ„±λœ DDL 은 개발 μž₯λΉ„μ—μ„œλ§Œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.
  • μƒμ„±λœ DDL 은 μš΄μ˜μ„œλ²„μ—μ„œλŠ” μ‚¬μš©ν•˜μ§€ μ•Šκ±°λ‚˜, 적절히 닀듬은 ν›„ μ‚¬μš©ν•  수 μžˆλ‹€.

λ°μ΄ν„°λ² μ΄μŠ€ μŠ€ν‚€λ§ˆ μžλ™ 생성 - 속성 μ„€μ •

JPA μ„€μ • μ‹œ hibernate.hbm2ddl.auto 섀정이 μ‘΄μž¬ν•˜λŠ”λ°, μ•„λž˜μ™€ 같은 κΈ°λŠ₯듀을 μ œκ³΅ν•œλ‹€.

  • create κΈ°μ‘΄ ν…Œμ΄λΈ”μ„ DROP ν•œ ν›„ λ‹€μ‹œ CREATE ν•œλ‹€.
  • create-drop create 와 κ°™μœΌλ‚˜ μ’…λ£Œ μ‹œμ μ— ν…Œμ΄λΈ”μ„ DROP ν•œλ‹€.
  • update λ³€κ²½λ¬Έλ§Œ λ°˜μ˜ν•œλ‹€. λ•Œλ¬Έμ— 운영 DB μ—μ„œλŠ” μ‚¬μš©ν•΄μ„  μ•ˆλœλ‹€.
  • validate 엔티티와 ν…Œμ΄λΈ”μ΄ 정상 λ§€ν•‘λ˜μ—ˆλŠ”μ§€λ§Œ ν™•μΈν•œλ‹€.
  • none 아무 섀정도 μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€.

λ°μ΄ν„°λ² μ΄μŠ€ μŠ€ν‚€λ§ˆ μžλ™ 생성 - 주의

운영 μž₯λΉ„μ—λŠ” μ ˆλŒ€λ‘œ create create-drop update λŠ” μ‚¬μš©ν•˜λ©΄ μ•ˆλœλ‹€.

  • 개발 초기 λ‹¨κ³„μ—λŠ” create λ˜λŠ” update λ₯Ό μ‚¬μš©ν•œλ‹€.
  • ν…ŒμŠ€νŠΈ μ„œλ²„μ—μ„œλŠ” update λ˜λŠ” validate λ₯Ό μ‚¬μš©ν•œλ‹€.
  • μŠ€ν…Œμ΄μ§•κ³Ό 운영 μ„œλ²„μ—μ„œλŠ” validate λ˜λŠ” none 을 μ‚¬μš©ν•œλ‹€.

DDL 생성 κΈ°λŠ₯

DDL 생성 κΈ°λŠ₯은 DDL 을 μžλ™μœΌλ‘œ 생성할 λ•Œλ§Œ μ‚¬μš©λ  뿐, JPA 의 μ‹€ν–‰ λ‘œμ§μ—λŠ” 영ν–₯을 주지 μ•ŠλŠ”λ‹€.

  • μ œμ•½μ‘°κ±΄ μΆ”κ°€
    • @Column(nullable = false, length = 10)
  • μœ λ‹ˆν¬ μ œμ•½μ‘°κ±΄ μΆ”κ°€
    • @Table(uniqueConstraints = {@UniqueConstraint( name = "NAME_AGE_UNIQUE", columnNames = {"NAME", "AGE"} )})

ν•„λ“œμ™€ 컬럼 맀핑


@Entity
public class Member {
    @Id // PK 맀핑
    private Long id;
    @Column(name = "name") // 컬럼 이름 μ„€μ •
    private String userName;
    private Integer age;
    @Enumerated(EnumType.STRING) // enum νƒ€μž… μ‚¬μš© μ‹œ
    private RoleType roleType;
    @Temporal(TemporalType.TIMESTAMP) // λ‚ μ§œ νƒ€μž… μ‚¬μš© μ‹œ
    private Date createdDate;
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;
    @Lob
    private String description;
    public Member() {
    }
}
create table Member (
   id bigint not null,
    age integer,
    createdDate timestamp,
    description clob,
    lastModifiedDate timestamp,
    roleType varchar(255),
    name varchar(255),
    primary key (id)
)

맀핑 μ• λ„ˆν…Œμ΄μ…˜ 정리

  • @Column 컬럼 맀핑
  • @Temporal λ‚ μ§œ 맀핑
  • @Enumerated enum νƒ€μž… 맀핑
  • @Lob BLOB, CLOB 맀핑
  • @Transient νŠΉμ • ν•„λ“œλ₯Ό μ»¬λŸΌμ— λ§€ν•‘ν•˜μ§€ μ•ŠμŒ

@Column 속성

  • name ν•„λ“œμ™€ 맀핑할 ν…Œμ΄λΈ”μ˜ 컬럼 이름
    • 기본값은 객체의 ν•„λ“œ μ΄λ¦„μœΌλ‘œ 지정됨
  • insertable updatable 등둝, λ³€κ²½ κ°€λŠ₯ μ—¬λΆ€
    • 기본값은 TRUE 둜 지정됨
  • nullable(DDL) null κ°’μ˜ ν—ˆμš© μ—¬λΆ€ μ„€μ •ν•œλ‹€. false 둜 μ„€μ • μ‹œ not null μ œμ•½μ‘°κ±΄μ΄ μΆ”κ°€λœλ‹€.
  • unique(DDL) ν•œ μ»¬λŸΌμ— κ°„λ‹¨νžˆ μœ λ‹ˆν¬ μ œμ•½μ‘°κ±΄ μΆ”κ°€ μ‹œ μ‚¬μš©
    • 이름을 μ„€μ •ν•˜κΈ° μœ„ν•΄ @Table(uniqueConstraints) 더 많이 μ‚¬μš©ν•¨
  • columnDefinition(DDL) DB 컬럼 정보λ₯Ό 직접 쀄 수 있음
  • length(DDL) 문자 길이 μ œμ•½μ‘°κ±΄. String νƒ€μž…μ—λ§Œ μ‚¬μš©
    • 기본값은 255 둜 지정됨
  • precision scale(DDL) BigDecimal νƒ€μž…μ—μ„œ μ‚¬μš©
    • precision 은 μ†Œμˆ˜μ  포함 전체 자릿수. κΈ°λ³Έκ°’ = 19
    • scale 은 μ†Œμˆ˜μ˜ 자릿수. κΈ°λ³Έκ°’ = 2

@Enumarated 속성

μžλ°” enum νƒ€μž…μ„ 맀핑할 λ•Œ μ‚¬μš©ν•˜λŠ”λ°, ORDINAL 은 μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€. ORDINAL 은 μˆœμ„œλ‘œ μ €μž₯ν•˜κΈ° λ•Œλ¬Έμ— enum 이 좔가될 경우 같은 값을 보μž₯ν•  수 μ—†λ‹€.

  • @Enumerated(EnumType.ORDINAL) enum μˆœμ„œλ₯Ό DB 에 μ €μž₯
  • @Enumerated(EnumType.STRING) enum 이름을 DB 에 μ €μž₯

@Temporal 속성

λ‚ μ§œ νƒ€μž…μ„ 맀핑할 λ•Œ μ‚¬μš©ν•œλ‹€. μ΅œμ‹  ν•˜μ΄λ²„λ„€μ΄νŠΈμ—μ„œλŠ” LocalDate, LocalDateTime 을 μ§€μ›ν•˜κΈ° λ•Œλ¬Έμ— μƒλž΅ κ°€λŠ₯ν•˜λ‹€.

  • @Temporal(TemporalType.DATE) λ‚ μ§œ, DB 의 date νƒ€μž…κ³Ό 맀핑
  • @Temporal(TemporalType.TIME) μ‹œκ°„, DB 의 time νƒ€μž…κ³Ό 맀핑
  • @Temporal(TemporalType.TIMESTAMP) λ‚ μ§œμ™€ μ‹œκ°„, DB 의 timestamp νƒ€μž…κ³Ό 맀핑

@Lob 속성

DB 의 BLOC κ³Ό CLOB νƒ€μž…κ³Ό 맀핑할 λ•Œ μ‚¬μš©ν•œλ‹€. λ§€ν•‘ν•˜λŠ” ν•„λ“œ νƒ€μž…μ΄ 문자면 CLOB 맀핑, λ‚˜λ¨Έμ§€λŠ” BLOB λ§€ν•‘λœλ‹€.

  • CLOB - String char[] java.sql.CLOB
  • BLOB - byte[] java.sql.BLOB

κΈ°λ³Έ ν‚€ 맀핑


κΈ°λ³Έ ν‚€ 맀핑 방법

  • @Id 만 μ‚¬μš©ν•˜μ—¬ 직접 ν• λ‹Ή
  • @Id @GeneratedValue λ₯Ό ν†΅ν•œ μžλ™ 생성
    • IDENTITY DB 에 μœ„μž„, MySQL μ—μ„œ μ‚¬μš©ν•œλ‹€.
    • SEQUENCE DB μ‹œν€€μŠ€ 였브젝트 μ‚¬μš©, ORACLE μ—μ„œ μ‚¬μš©ν•œλ‹€.
      • @SequenceGenerator ν•„μš”
    • TABLE ν‚€ μƒμ„±μš© ν…Œμ΄λΈ” μ‚¬μš©, λͺ¨λ“  DB μ—μ„œ μ‚¬μš©ν•œλ‹€.
      • @TableGenerator ν•„μš”
    • AUTO 방언에 따라 μžλ™ 지정, κΈ°λ³Έκ°’μœΌλ‘œ μ„€μ •λœλ‹€.

IDENTITY μ „λž΅

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}

κΈ°λ³Έ ν‚€ 생성을 DB 에 μœ„μž„ν•˜λŠ” 것이닀. 주둜 MySQL 의 AUTO_INCREMENT 에 μ‚¬μš©λœλ‹€. JPA λŠ” 보톡 νŠΈλžœμž­μ…˜ 컀밋 μ‹œμ μ— INSERT SQL 을 μ‹€ν–‰ν•œλ‹€. ν•˜μ§€λ§Œ, AUTO_ INCREMENT λŠ” λ°μ΄ν„°λ² μ΄μŠ€μ— INSERT SQL 을 μ‹€ν–‰ν•œ 이후에 ID 값을 μ•Œ 수 μžˆλ‹€. λ•Œλ¬Έμ—, IDENTITY μ „λž΅μ€ em.persist() μ‹œμ μ— μ¦‰μ‹œ INSERT SQL μ‹€ν–‰ν•˜κ³  DBμ—μ„œ μ‹λ³„μžλ₯Ό μ‘°νšŒν•œλ‹€.

SEQUENCE μ „λž΅

@Entity
@SequenceGenerator(
    name = β€œMEMBER_SEQ_GENERATOR",
    sequenceName = β€œMEMBER_SEQ", //맀핑할 λ°μ΄ν„°λ² μ΄μŠ€ μ‹œν€€μŠ€ 이름
    initialValue = 1, allocationSize = 1)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                    generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
}

DB μ‹œν€€μŠ€λŠ” μœ μΌν•œ 값을 μˆœμ„œλŒ€λ‘œ μƒμ„±ν•˜λŠ” νŠΉλ³„ν•œ DB μ˜€λΈŒμ νŠΈμ΄λ‹€. 주둜 ORACLE μ—μ„œ μ‚¬μš©ν•œλ‹€. SEQUENCE μ „λž΅μ€ allocationSize 속성을 톡해 λ©”λͺ¨λ¦¬μ— 숫자λ₯Ό 미리 κ°€μ Έμ™€μ„œ μ‚¬μš©ν•œλ‹€. 즉, 50으둜 섀정해두면, 1 ~ 50 κΉŒμ§€λŠ” λ©”λͺ¨λ¦¬λ₯Ό 톡해 id 값을 ν• λ‹Ήν•˜κΈ° λ•Œλ¬Έμ— DB 와 λ„€νŠΈμ›Œν¬ 톡신이 μΌμ–΄λ‚˜μ§€ μ•ŠλŠ”λ‹€.

TABLE μ „λž΅

create table MY_SEQUENCES (
    sequence_name varchar(255) not null,
    next_val bigint,
    primary key ( sequence_name )
)
@Entity
@TableGenerator(
    name = "MEMBER_SEQ_GENERATOR",
    table = "MY_SEQUENCES",
    pkColumnValue = β€œMEMBER_SEQ", allocationSize = 1)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE,
                    generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
}

ν‚€ 생성 μ „μš© ν…Œμ΄λΈ”μ„ ν•˜λ‚˜ λ§Œλ“€μ–΄μ„œ DB μ‹œν€€μŠ€λ₯Ό ν‰λ‚΄λ‚΄λŠ” μ „λž΅μ΄λ‹€. λͺ¨λ“  DB 에 적용 κ°€λŠ₯ν•˜μ§€λ§Œ, μ„±λŠ₯이 쒋지 μ•ŠκΈ° λ•Œλ¬Έμ— 잘 μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€.

ꢌμž₯ν•˜λŠ” μ‹λ³„μž μ „λž΅

κ°€μž₯ 쒋은 κΈ°λ³Έ ν‚€ μ œμ•½ 쑰건은 null 이 μ•„λ‹ˆλ©° μœ μΌν•˜κ³  λ³€ν•˜λ©΄ μ•ˆλœλ‹€. λ―Έλž˜κΉŒμ§€ 이 쑰건을 λ§Œμ‘±ν•˜λŠ” μžμ—°ν‚€λŠ” μ°ΎκΈ° μ–΄λ ΅κΈ° λ•Œλ¬Έμ— λŒ€μ²΄ν‚€λ₯Ό μ‚¬μš©ν•œλ‹€. 예λ₯Ό λ“€μ–΄, μ£Όλ―Όλ“±λ‘λ²ˆν˜Έλ„ κΈ°λ³Έ ν‚€λ‘œ μ μ ˆν•˜μ§€ μ•Šλ‹€. λ•Œλ¬Έμ—, Longν˜• + λŒ€μ²΄ν‚€ + ν‚€ μƒμ„±μ „λž΅ μ‚¬μš©ν•˜λŠ” 것을 ꢌμž₯ν•œλ‹€.

μ‹€μ „ 예제 1 - μš”κ΅¬μ‚¬ν•­ 뢄석과 κΈ°λ³Έ 맀핑


μš”κ΅¬μ‚¬ν•­ 뢄석

  • νšŒμ›μ€ μƒν’ˆμ„ μ£Όλ¬Έν•  수 μžˆλ‹€.
  • μ£Όλ¬Έ μ‹œ μ—¬λŸ¬ μ’…λ₯˜μ˜ μƒν’ˆμ„ 선택할 수 μžˆλ‹€.

κΈ°λŠ₯ λͺ©λ‘

  • νšŒμ› κΈ°λŠ₯
    • νšŒμ› 등둝
    • νšŒμ› 쑰회
  • μƒν’ˆ κΈ°λŠ₯
    • μƒν’ˆ 등둝
    • μƒν’ˆ μˆ˜μ •
    • μƒν’ˆ 쑰회
  • μ£Όλ¬Έ κΈ°λŠ₯
    • μƒν’ˆ μ£Όλ¬Έ
    • μ£Όλ¬Έ λ‚΄μ—­ 쑰회
    • μ£Όλ¬Έ μ·¨μ†Œ

도메인 λͺ¨λΈ 뢄석

  • νšŒμ›κ³Ό 주문의 관계: νšŒμ›μ€ μ—¬λŸ¬ 번 μ£Όλ¬Έν•  수 μžˆλ‹€. (μΌλŒ€λ‹€)
  • μ£Όλ¬Έκ³Ό μƒν’ˆμ˜ 관계: μ£Όλ¬Έν•  λ•Œ μ—¬λŸ¬ μƒν’ˆμ„ 선택할 수 μžˆλ‹€. λ°˜λŒ€λ‘œ 같은 μƒν’ˆλ„ μ—¬λŸ¬ 번 주문될 수 μžˆλ‹€. μ£Όλ¬Έ μƒν’ˆμ΄λΌλŠ” λͺ¨λΈμ„ λ§Œλ“€μ–΄ λ‹€λŒ€λ‹€ 관계λ₯Ό μΌλŒ€λ‹€, λ‹€λŒ€μΌ κ΄€κ³„λ‘œ 풀어냄.

ν…Œμ΄λΈ” 섀계

μ—”ν‹°ν‹° 섀계와 맀핑

데이터 쀑심 μ„€κ³„μ˜ 문제점

ν˜„μž¬ 방식은 ν…Œμ΄λΈ”μ˜ μ™Έλž˜ν‚€λ₯Ό 객체에 κ·ΈλŒ€λ‘œ 가져와 μ‚¬μš©ν•œλ‹€. 즉, ν…Œμ΄λΈ” 섀계에 λ§žμΆ°μ§„ 객체λ₯Ό μ‚¬μš©ν•˜κ³  μžˆλŠ” 것이닀. μ΄λŸ¬ν•œ μ„€κ³„λŠ” 객체 κ·Έλž˜ν”„ 탐색이 λΆˆκ°€λŠ₯ν•˜λ©°, μ°Έμ‘°κ°€ μ—†κΈ° λ•Œλ¬Έμ— UML 도 잘λͺ»λœ 것을 μ•Œ 수 μžˆλ‹€.