엔티티 매핑

김준석·2021년 1월 10일
2
post-thumbnail

JPA를 사용하는 데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것이다. 따라서 매핑 어노테이션을 숙지하고 사용해야 한다.

  • 객체와 테이블 매핑: @Entity, @Table
  • 기본 키 매핑: @Id
  • 필드와 컬럼 매핑: @Column
  • 연관관계 매핑: @ManyToOne, @JoinColumn

1. @Entity

@Entity 어노테이션은 JPA를 사용해서 테이블과 매핑해야하는 클래스에 필수로 붙여야 한다.

속성

  • name
    • 기본값: 클래스 이름 사용
    • JPA에서 사용할 엔티티 이름을 지정
    • 만약 다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충돌하지 않도록 해야 한다.

주의사항

  • 기본 생성자 필수.

    • 자바는 생성자가 없으면 기본생성자를 자동 생성한다.

      public Member() {} // 기본 생성자
    • 생성자를 하나 이상 만들면 기본 생성자를 자동으로 만들지 않는다.

      // 직접 만든 기본 생성자
      public Member () {}
      
      public Member (String name) {
          this.name = namw
      }
  • final 클래스, enum, interface, inner 클래스에는 사용 불가.

  • 저장할 필드에 final을 사용하면 안 됨.

2. @Table

@Table 어노테이션은 엔티티와 매핑할 테이블을 지정한다. 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.

속성

  • name
    • 기본값: 엔티티 이름 사용
    • 매핑할 테이블 이름
  • catalog
    • catalog 기능이 있는 데이터베이스에서 catalog 매핑
  • schema
    • schema 기능이 있는 데이터베이스에서 schema 매핑
  • uniqueConstraints(DDL)
    • DDL 생성 시에 유니크 제약조건을 만든다. (2개 이상의 복합 유니크 제약조건도 가능)
    • 스키마 자동 생성 기능을 사용해서 DDL를 만들 때만 사용

3. 다양한 매핑 사용

간단히 어떤 것들이 있는지 확인해보자. (자세한 설명은 7. 필드와 컬럼 매핑: 레퍼런스에서 😊)

  • 회원은 일반 회원과 관리자로 구분

    @Enumerated(EnumType.STRING)
    private RoleType roleType;
    // RoleType
    public enum RoleType {
        ADMIN, USER
    }

    자바의 Enum은 @Enumerated 어노테이션을 사용하여 매핑

  • 회원 가입일과 수정일 존재

    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;
    
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;

    자바의 날씨 타입은 @Temporal 어노테이션을 사용하여 매핑

  • 회원을 설명할 수 있는 필드 존재 (길이 제한 없음)

    @Lob
    private String description;

    길이 제한이 없는 필드는 @Lob 어노테이션을 사용하여 매핑

4. 데이터베이스 스키마 자동 생성

JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다. 클래스의 매핑 정보를 보면 어떤 테이블에 어떤 컬럼을 사용하는지 알 수 있다.

예시

<property name="hibernate.hbm2ddl.auto" value="create" />

persistence.xml에 이 속성을 추가하면 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다(create).

hibernate.hbm2ddl.auto 속성

  • create
    • 기존 테이블을 삭제하고 새로 생성(DROP + CREATE)
  • create-drop
    • create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거(DROP + CREATE + DROP)
  • update
    • 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정
  • validate
    • 데이터베이스 테이블과 엔티티 매핑정보를 비교
    • 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않음
    • 이 설정은 DDL을 수정하지 않음
  • none
    • 자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭제 또는 유효하지 않은 옵션 값을 줘야 함
    • none은 유효하지 않은 옵션 값

HBM2DDL 주의사항

운영 서버에서 create, create-drop처럼 DDL을 수정하는 옵션은 절대 사용하면 안 된다. 오직 개발 서버나 개발 단계에서만 사용해야 한다. 이 옵션들은 운영 중인 데이터베이스의 테이블이나 컬럼을 삭제할 수 있다.

5. DDL 생성 기능

@Entity
@Table(name="MEMBER")
public class Member {
    
    @Id
    @Column(name = "ID")
    private String id;
    
    @Column(name = "NAME")
    private String username;
    
    // ...
}

이 엔티티는 어떤 DDL이 생성될까?

create table MEMBER (
    ID varcher(255) not null,
    NAME varchar(255),
    ...
    primary key (ID)
)

여기서 제약조건이 추가되었다고 생각하자.

  • 회원 이름은 필수
  • 회원 이름은 10자를 초과하면 안 된다.

그럼 엔티티는 이렇게 수정될 것이다.

@Entity
@Table(name="MEMBER")
public class Member {
    
    @Id
    @Column(name = "ID")
    private String id;
    
    @Column(name = "NAME", nullable = false, length = 10)
    private String username;
    
    // ...
}

그럼 어떤 DDL이 생성될까?

create table MEMBER (
    ID varcher(255) not null,
    NAME varchar(10) not null,
    ...
    primary key (ID)
)

NAME 컬럼은 보면 not null이 추가되었고, varchar 조건이 10으로 바뀐 것을 확인할 수 있다. 자세한 것은 뒤에서 설명하겠다.

이런 기능들은 단지 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.

6. 기본 키 매핑

  • 직접 할당: 기본 키를 애플리케이션에서 직접 할당
  • 자동 생성: 대리 키 사용 방식
    • IDENTITY: 기본 키 생성을 데이터베이스에 위임
    • SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키 할당
    • TABLE: 키 생성 테이블을 사용

기본 키 직접 할당 전략

직접 할당 예시

@Id
@Column(name = "id")
private String id;

@Id 적용 가능 자바 타입

  • 자바 기본형
  • 자바 래퍼(Wrapper)형
  • String
  • java.util.Date
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger

IDENTITY 전략

기본 키 생성을 데이터베이스에 위임하는 전략이다.

데이터베이스에 값을 저장할 때 ID 칼럼을 비워두면 데이터베이스가 순서대로 값을 채워준다.

@Entity
public class Board {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 추가
    private Long id;
    
    // ...
}

SEQUENCE 전략

데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다. SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다.

시퀀스 생성

CREATE TABLE BOARD (
    ID BIGINT NOT NULL PRIMARY KEY,
    DATA VARCHER(255)
)

// 시퀀스 생성
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;

시퀀스 매핑 코드

@Entity
@SequenceGenerator(
    name = "BOARD_SEQ_GENERATOR",
    sequenceName = "BOARD_SEQ", // 매핑할 데이터베이스 시퀀스 이름
    initialValue = 1, allocationSize = 1)
public class Board {
    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                   generator = "BOARD_SEQ_GENERATOR")
    private Long id;
    
    // ...
}

IDENTITY 전략과 SEAUENCE 전략의 차이

  • IDENTITY 전략
    • 엔티티를 데이터베이스에 저장한 후에 식별자를 조회해서 엔티티의 식별자에 할당
  • SEQUENCE 전략
    • 먼저 데이터베이스 시퀀스를 사용해서 식별자 조회
    • 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장
    • 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장

@SequenceGenerator

  • name
    • 필수
    • 식별자 생성기 이름
  • sequenceName(하이버네이트 기준)
    • 기본값: hibernate_sequence
    • 데이터베이스에 등록되어 있는 시퀀스 이름
  • initialValue
    • 기본값: 1
    • DDL 생성 시에만 사용됨
    • 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정
  • allocationSize
    • 기본값: 50
    • 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨)
  • catalog, schema
    • 데이터베이스 catalog, schema 이름

Table 전략

키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다.

키 생성 용도 테이블

create table MY_SEQUENCE (
    sequence_name varchar(255) not null,
    next_val bigint,
    primary key (sequence_name)
)

테이블 전략 매핑 코드

@Entity
@TableGenerator(
    name = "BOARD_SEQ_GENERATOR",
    table = "MY_SEQUENCES",
    pkColumnValue = "BOARD_SEQ", allocationSize = 1)
public class Board {
    
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE,
                   generator = "BOARD_SEQ_GENERATOR")
    private Long id;
    
    // ...
}

시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작방식이 같다.

@TableGenerator

  • name
    • 필수
    • 식별자 생성기 이름
  • table(하이버네이트 기준)
    • 기본값: hibernate_sequences
    • 키생성 테이블명
  • pkColumnName(하이버네이트 기준)
    • 기본값: sequence_name
    • 시퀀스 컬럼명
  • valueColumnName(하이버네이트 기준)
    • 기본값: next_val
    • 시퀀스 값 컬럼명
  • pkColumnValue
    • 기본값: 엔티티 이름
    • 기로 사용할 값 이름
  • initialValue
    • 기본값: 0
    • 초기 값
    • 마지막으로 생성된 값이 기준
  • allocationSize
    • 기본값: 50
    • 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨)
  • catalog, schema
    • 데이터베이스 catalog, schema 이름
  • uniqueConstraints(DDL)
    • 유니크 제약 조건을 지정

AUTO 전략

GenerationType.AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SWQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.

@Entity
public class Board {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    // ...
}

장점

  • 데이터베이스를 변경해도 코드를 수정할 필요없음

7. 필드와 컬럼 매핑: 레퍼런스

@Column

객체 필드를 테이블 컬럼에 매핑한다.

@Enumerated

자바의 enum 타입을 매핑할 때 사용한다.

속성

  • value
    • EnumType.ORDINAL: 순서를 데이터베이스에 저장
    • EnumType.STRING: 이름을 데이터베이스에 저장

@Temporal

날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용한다.

속성

  • value
    • TemporalType.DATE: 날짜, 데이터베이스 date 타입과 매핑 (2021-01-17)
    • TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑 (11:43:11)
    • TemporalType.TIMESTAMP: 날짜와 시간, 데이터베이스 timestamp 타입과 매핑 (2021-01-17 11:43:11)

@Lob

데이터베이스 BLOB, CLOB 타입과 매핑한다.

매핑하는 필드 타입이 문자면 CLOB으로 매핑하고 나머지는 BLOB로 매핑한다.

@Transient

이 필드는 매핑하지 않는다.

데이터베이스에 저장하지도 조회하지도 않는다. 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.

@Access

JPA가 엔티티 데이터에 접근하는 방식을 지정한다.

@Access를 설정하지 않으면 @Id의 위치를 기분으로 접근 방식이 설정된다.

  • 필드 접근: AccessType.FIELD

    @Entity
    @Access(AccessType.FIELD)
    public class Member {
        
        @Id
        private String id;
        
        private String data1;
        private String data2;
        
        // ...
    }
  • 프로퍼티 접근: AccessType.PROPERTY

    @Entity
    @Access(AccessType.PROPERTY)
    public class Member {
        
        private String id;
        private String data1;
        private String data2;
        
        @Id
        public String getId() {
            return id;
        }
        
        @Column
        public String getData1() {
            return data1;
        }
        
        @Column
        public String getData2() {
            return data2;
        }
    }
profile
내 몸에는 꼰대의 피가 흐른다.

1개의 댓글

comment-user-thumbnail
2021년 1월 10일

👍👍

답글 달기