개요
이번 게시글에서는 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: 어떠한 것도 적용하지 않음
- create: 기존 테이블을 DROP 한 후 CREATE (종료 시점 이후에도 테이블은 유지)
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 |