DB/JPA

[JPA] Entity 매핑 정리

꾸준함. 2021. 8. 23. 01:20

개요

이번 게시글에서는 JPA 엔티티 매핑 관련 어노테이션에 대해 정리해보겠습니다.

연관 관계와 관련된 어노테이션은 다음 게시글에서 정리할 예정이고 해당 포스팅에서는 아래의 매핑에 대해서 정리해보겠습니다.

  • 객체와 테이블 매핑
  • 필드와 칼럼 매핑
  • 기본 키 매핑

 

1. 객체와 테이블 매핑

객체와 테이블 매핑할 때는 두 가지 어노테이션이 사용됩니다.

  • @Entity
  • @Table

 

1.1 @Entity

  • JPA를 사용해 테이블과 매핑하기 위해서는 클래스에 필수로 @Entity 어노테이션을 붙여줘야 함
  • 파라미터가 없는 기본 생성자를 필수로 정의해야 함
  • final 클래스, enum, interface, inner 클래스 사용 불가
    • enum 필드를 사용하기 위해서는 @Enumerated 어노테이션을 사용해야 함 (추후 설명 예정)
  • 또한, 저장할 필드에 final 키워드 사용 불가

 

1.2 @Table

  • @Table 어노테이션은 엔티티와 매핑할 테이블을 지정
  • @Table 어노테이션의 속성
    • name: 매핑할 테이블명이며 default 값은 엔티티 이름으로 사용
    • catalog: DB catalog 매핑
    • schema: DB schema 매핑
    • uniqueConstraints: DDL 생성 시 Unique 제약 조건 생성

 

1.3 DB 스키마 자동 생성

  • applicaiton.yml과 같은 설정 파일에 설정된 속성에 따라 @Entity, @Table 어노테이션이 부여된 클래스에 대한 DDL이 애플리케이션 실행 시점에 자동 생성됨
    • 설정 파일 내 Dialect 타입에 따라 DDL이 생성이 되는데 예시는 아래와 같음
    • ORACLE에서는 문자열 타입이 varchar2, MySQL에서는 문자열 타입이 varchar
    • JPA는 이를 인지하여 DDL을 적절하게 생성
  • DDL 자동생성 속성 (hibernate.hbm2ddl.auto)
    • create: 기존 테이블을 DROP 한 후 CREATE (종료 시점 이후에도 테이블은 유지)
      • field가 수시로 추가/삭제가 되는 개발 시점에서 유용한 속성
    • create-drop: create 옵션과 유사하나 종료 시점에 테이블을 DROP
    • update: 기존 테이블을 DROP하지 않고 변경 분만 반영
      • 상용 서버에 적용하기에는 위험하지만 개발 혹은 tb 서버에 적용하는 케이스가 있음
    • validate: 엔티티와 테이블이 DB와 정상적으로 매핑되었는지 확인
    • none: 어떠한 것도 적용하지 않음

 

1.3.1 DDL 자동생성 속성 적용 시 주의할 점

  • 상용 서버에서는 절대로 create, create-drop, update 속성을 적용하면 안 됨
    • create, create-drop 속성을 적용할 경우 기존 데이터가 날아가는 초비상 사태 발생
    • update 속성은 괜찮다고 생각할 수 있으나 ALTER COLUMN 명령어가 적용될 경우 Lock 걸릴 수 있음
  • 정리를 하자면 개발 초기 단계에는 create/update 속성을 적용, tb 서버에는 update 혹은 validate, 사용 서버에는 validate 또는 none을 적용하는 것을 추천
    • 상용 서버에 대해서는 최대한 신중하게 접근해야 하므로 속성을 none으로 적용하고 본인이 직접 작성한 쿼리를 개발 서버에 반영한 뒤 이상이 없을 경우 사용 서버에 적용하는 것을 추천

 

* DDL 생성 기능은 DDL을 자동 생성할 때만 적용되고 JPA의 실행 로직에는 영향을 주지 않음

 

2. 필드와 칼럼 매핑

필드와 칼럼 매핑할 때 사용되는 어노테이션은 아래와 같이 크게 5가지가 있습니다.

  • @Column: 칼럼 매핑
  • @Temporal: 날짜 타입 매핑 (Java8부터는 사용하지 않아도 됨)
  • @Enumerated: enum 타입 매핑
  • @Lob: BLOB, CLOB 매핑
  • @Transient: 클래스 내 필요한 필드지만 칼럼에 매핑하고 싶지 않을 때 사용 (매핑 무시)

 

2.1 @Column

@Column 어노테이션은 칼럼과 필드를 매핑할 때 사용되고 속성은 아래와 같습니다.

  • name: 필드와 매핑할 테이블의 칼럼명을 지정하는 속성이며 default 값은 객체의 필드명
    • Java의 경우 보통 컨벤션으로 카멜 케이스를 쓰고 DB 필드명은 보통 스네이크 케이스를 쓰는데 SpringBoot에서는 이를 인지하고 필드명을 카멜 케이스로 쓰더라도 알아서 스네이크 케이스로 변환해줌
  • insertable, updatable: 등록/변경 가능 여부를 지정하는 속성이며 default 값은 true
  • nullable: DDL 생성 시 null 값의 허용 여부를 설정하며 nullable을 false로 지정할 경우 DDL 내 해당 칼럼에 not null 제약 조건이 붙음
  • unique: 유니크 제약 조건을 거는 데 사용되는 속성인데 보통은 @UniqueConstraint 어노테이션을 대신 씀
  • columnDefinition: DDL 생성 시 DB 칼럼 정보를 직접 부여하는 속성
    • ex) varchar(255) default '공백'
  • length: DDL 생성 시 문자 길이 제약 조건을 부여하는 속성이며 문자열 String에만 적용
  • precision, scale: DDL 생성 시 BigDecimal 타입에 사용되며 precision은 소수점을 포함한 전체 자릿수, scale은 소수의 자릿수에 적용 (double, float 타입에는 적용 X)

 

2.2 @Temporal

날짜 타입을 매핑할 때 사용이 되는데 이는 Java8에 LocalDate, LocalDateTime이 도입되면서 생략이 가능해짐

 

2.3 @Enumerated

  • 자바 ENUM 타입을 매핑할 때 사용
  • @Enumerated 속성으로 value가 있으며 두 가지로 나뉨
    • EnumType.ORDINAL: enum 순서를 DB에 저장 (Integer type)
    • EnumType.STRING: enum명을 DB에 저장 (String type)
  • EnumType.ORDINAL 같은 경우 Enum 필드가 추가/변경될 경우 매핑이 꼬일 확률이 크므로 가급적 사용 자제
    • ex) Enum이 추가/삭제되어 순서가 뒤바뀔 경우 감당 못함

 

2.4 @Lob

  • 데이터베이스 BLOB, CLOB 타입과 매핑할 때 사용되며 별도 지정할 수 있는 속성 없음
  • 매핑할 수 있는 타입에 따라 매핑 타입이 다름
    • 매핑하는 필드 타입이 문자면 CLOB 매핑
    • 나머지는 BLOB 매핑

 

2.5 @Transient

  • 메모리 상에서만 임시로 값을 보관하고 싶을 때 사용되는 어노테이션
  • 필드 매핑이 안되며
  • DB에 저장 및 조회도 안됨

 

3. 기본키 매핑

기본키 매핑은 @Id 어노테이션을 통해 가능하며, AUTO_INCREMENT 속성과 같이 자동으로 아이디를 부여하는 어노테이션으로는 @GeneratedValue가 있습니다.

 

3.1 기본 키 매핑 방법

  • 직접 할당할 경우: @Id 어노테이션만 사용
  • 자동 생성할 경우: @Id 어노테이션과 함께 @GeneratedValue 어노테이션도 사용
  • GeneratedValue에는 크게 4가지 전략이 있음
    • IDENTITY: 기본 키 생성을 DB에 위임하는 방법, ex) MySQL
    • SEQUENCE: DB 시퀀스 오브젝트 사용, ex) ORACLE
    • TABLE: 키 생성용 테이블을 별도 생성하여 사용하며 모든 DB에 지정 가능
    • AUTO: Dialect에 따라 자동 지정하는 방법이며 GeneratedValue의 default 전략

 

3.1.1 IDENTITY 전략

  • 기본 키 생성을 DB에 위임하는 방법이며 MySQL, PostgreSQL과 같은 DB에서 사용
    • ex) MySQL의 AUTO_INCREMENT
  • JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL을 실행하는데 IDENTITY 전략 같은 경우 commit 시점이 아닌  persist 할 때마다 쿼리가 실행됨
    • 이는 PK 값을 DB에 값이 들어가야 알 수 있기 때문에 이렇게 실행이 됨
    • 따라서, JPA의 장점 중 하나인 지연 쓰기를 할 수 없음

 

@Entity
public class ExampleClass {
	
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    // 중략
}

 

3.1.2 SEQUENCE 전략

  • DB 시퀀스는 오라클 시퀀스와 같이 유일한 값을 순서대로 생성하는 특별한 DB 오브젝트
    • ORACLE, PostgreSQL과 같은 DB에서 사용
    • @SequenceGenerator가 반드시 필요
  • IDENTITY 전략과 달리 persist할 때마다 시퀀스 값만 받아오고 SQL Storage에 쌓아놓음
    • 쓰기 지연 지원
    • 그래도 persist할 때마다 시퀀스 값을 받아오면 네트워크를 매번 타서 성능 저하를 야기하지 않을까?라는 고민이 생김 (이때 등장하는 개념이 allocationSize 속성이며 이는 추후 설명)
  • 쓰기 지연에 대한 내용은 https://jaimemin.tistory.com/1898 참고

 

3.1.2.1 @SequenceGenerator

SEQUENCE 전략에는 @SequenceGenerator가 반드시 필요하며 크게 6가지의 속성이 있습니다.

  • name: 필수로 지정해야 하는 값이며 식별자 생성기명
  • sequenceName: DB에 등록되어 있는 시퀀스명
  • initialValue: DDL 생성 시에만 사용되며, sequence DDL을 생성할 때 처음 시작하는 수를 지정
    • 디폴트 값은 1
  • allocationSize: sequence 한번 호출할 때마다 증가하는 수이며 디폴트 값은 50
    • 앞서 질문하였던 persist 할 때마다 시퀀스 값을 받아오면 네트워크를 매번 타서 성능 저하를 야기하지 않을까? 에 대한 해답
    • persist를 통해 sequence 값을 받아올 때 allocationSize만큼 sequence를 불러오고 메모리에 세팅
    • 만약 디폴트 값인 50으로 설정되어 있다면 한번 persist할 때 id를 1~50까지 불러오고 50번 persist할 때 동안 하나씩 부여하며 51번째 persist 때는 또 sequence 값을 51~100까지 불러온 뒤에 세팅하는 방식 (네트워크를 덜 타기 때문에 성능 저하 방지)
    • 동시성 이슈도 없기 때문에 여러 WAS가 있더라도 문제없음
  • catalog: DB catalog명
  • schema: DB schema명

 

@Entity
@SequenceGenerator(
	name = "EXAMPLECLASS_SEQ_GENERATOR",
    sequenceName = "EXAMPLECLASS_SEQ",
    initialValue = 1,
    allocationSize = 50
)
public class ExampleClass {
	
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
    	generator = "EXAMPLECLASS_SEQ_GENERATOR")
    private Long id;
    
    // 중략
}

 

3.1.3 TABLE 전략

  • 키 생성 전용 테이블을 생성하여 DB 시퀀스를 흉내 내는 전략
  • 모든 DB에 대해 적용할 수 있다는 것이 장점이지만
  • 타 전략보다 성능이 떨어지는 것이 단점
  • 성능이 떨어지기 때문에 상용 서버에서 해당 전략을 사용하는 경우는 드묾
    • 보통 사용 서버에 지정된 DB 컨벤션을 따름

 

* 자주 사용되는 전략은 아니니 추후 설명 생략

 

비고

권장하는 식별자 전략

  • 기본 키 제약 조건: NOT NULL이며 유일하고 변하면 안 되는 값
  • 위 제약 조건을 서비스가 운용되는 내내 만족하는 자연 키는 찾기 어렵기 때문에 랜덤으로 생성된 키와 같은 대리 키를 기본 키로 지정하는 것을 추천

 

테이블 중심 설계가 아닌 객체 중심 설계의 문제점

  • 테이블의 외래 키를 객체에 그대로 가져오기 때문에
  • 객체 그래프 탐색이 불가능하며
  • 참조가 없으므로 UML도 잘못됨

 

출처

자바 ORM 표준 JPA 프로그래밍 - 기본편 (김영한 강사님)

반응형

'DB > JPA' 카테고리의 다른 글

[JPA] 상속관계 매핑  (2) 2021.09.07
[JPA] 다양한 연관관계 매핑  (0) 2021.08.31
[JPA] 연관관계 매핑 간단 정리  (0) 2021.08.28
[JPA] PersistenceContext 간단 정리  (0) 2021.08.20
JPA를 학습해야하는 이유  (1) 2021.08.16