Springboot Jpa - 엔티티 매핑
엔티티를 매핑하는 방법을 알아보자.
객체와 테이블 매핑
매핑 소개
- 객체와 테이블 매핑:
@Entity
,@Table
- 필드와 컬럼 매핑:
@Column
- 기본 키 매핑:
@Id
- 연관관계 매핑:
@ManyToOne
,@JoinColumn
@Entity
@Entity
- JPA가 관리하는 엔티티가 된다.
- JPA를 사용해 테이블과 매핑할 클래스는 필수적으로 붙여줘야함
- 접근 제어자가 public, protected 중 하나인 기본 생성자가 필수적으로 있어야한다. JPA의 기본 스펙이기도 하지만, 리플렉션이나 프록시등에서 다뤄야하기 떄문이기도 하다.
final
,enum
,interface
,inner
클래스에서는 사용할 수 없다.- 저장할 필드에
final
을 사용할 수 없다. - JPA를 사용해 테이블과 매핑할 클래스는 필수적으로 붙여줘야함
속성
- name
- JPA에서 사용할 엔티티 이름을 지정한다.
- 기본 값으로는 클래스 이름을 그대로 사용한다.
- 같은 클래스 이름이 없으면, 되도록이면 기본값을 사용한다.
- 기본 값으로는 클래스 이름을 그대로 사용한다.
@Table
@Table
- 엔티티와 매핑할 테이블을 지정한다.
속성
- name
- 매핑할 테이블 이름을 지정한다.
- 기본 값으로는 클래스 이름을 그대로 사용한다.
- catalog
- DB catalog 매핑시 사용한다.
- schema
- DB schema 매핑시 사용한다.
- uniqueConstraints
- DDL 생성 시에 유니크 제약 조건 생성한다.
DB 스키마 자동 생성
개발 단계에서 DDL을 application 실행 시점에 자동 생성해주는 기능이다. DB 방언을 활용해서 DB에 맞는 적절한 DDL 생성해준다. application 런타임시(JPA의 실행 로직)에는 영양을 주지 않고, 실행 시점에서 DDL을 작성해주는 것만 해준다.
hibernate.hbm2ddl.auto
DB 스키마 자동생성 기능을 사용하기 위해서는 persistence.xml
에 <property name="hibernate.hbm2ddl.auto" value="[옵션]" />
를 추가하면된다.
hibernate.hbm2ddl.auto 옵션
- create
- 기존테이블 삭제 후 다시 생성한다.
DROP
->CREATE
- create-drop
- 기존테이블 삭제 후 다시 생성하는데, 종료시점에 테이블을 삭제한다.
DROP
->CREATE
-> (프로젝트 종료시)DROP
- update
- DB에 이미 테이블이 정의되어있어, 추가되어야하는 변경분만 반영한다.
- 변경 부분에 대해서만
ALTER
- DB에 없던 부분을 반영은 하지만, DB에 있던 내용을 삭제하지는 않는다.
- 운영에는 사용하면 안된다.
- 변경 부분에 대해서만
- validate
- 엔티티와 테이블이 정상 매핑되었는지만 확인해준다.
- 만약 정상 매핑이 되지 않았다면, 오류가 발생한다.
- none
- 이 기능을 사용하지 않는다.
<property name="hibernate.hbm2ddl.auto" value="[옵션]" />
을 삭제한 것과 같이 동작한다.
주의 데이터가 많이 쌓여있는 DB에서
ALTER
시, 모든 데이터에 대해 컬럼을 수정해야해서 데이터 양에 따라 다르지만 시간이 굉장히 오래 걸린다. 수정중에는 해당 table에 DB lock이 발생해 해당 table을 사용하는 다른 application에서 시스템 중단 상태가 발생할 수 있는 큰 문제가 발생할 수 있다. 그렇기에create
,create-drop
,update
는 사용에 굉장히 유의해야한다. 되도록이면, 로컬에서 개발시가 아니라면 사용하지 않는 것이 좋다.
필드와 컬럼 매핑
매핑 소개
@Column
: 컬럼 매핑@Temporal
: 날짜 타입 매핑@Enumerated
: enum 타입 매핑@Lob
: BLOB, CLOB 매핑@Transient
: 특정 필드를 컬럼에 매핑하지 않음(매핑 무시)
@Column
@Column
- 컬럼 이름을 매핑한다.
- 필드명과 컬럼명이 다를 때,
name
속성으로 컬럼명을 입력해 매핑한다. - 필드명과 컬럼명이 다를 때,
속성
- name
- 필드와 매핑할 테이블의 컬럼 이름을 지정한다.
- 기본 값 - 객체의 필드 이름
- insertable,updatable
- 컬럼을 수정했을 때, DB에 등록, 변경에 대한 내용을 반영할 것인지에 대한 여부를 설정한다.
- 기본 값 -
TRUE
- 기본 값 -
- nullable(DDL)
null
값의 허용 여부를 설정한다.false
로 설정시 DDL 생성 시에not null
제약조건이 붙는다.- DDL시 외에도, JPA에서도 런타임시 체크해준다.
- 기본 값 -
TRUE
- DDL시 외에도, JPA에서도 런타임시 체크해준다.
- unique(DDL)
@Table
의uniqueConstraints
와 같지만 한 컬럼에 간단히유니크 제약조건
을 걸 때 사용한다.- 잘 사용되지 않는 이유는 생성된 unique key의 이름이 랜덤으로 의미 없는 값으로 설정되기 때문이다.
@Table
의uniqueConstraints
는 key의 이름을 지정할 수 있어, 이 방식으로 더 사용된다.- 잘 사용되지 않는 이유는 생성된 unique key의 이름이 랜덤으로 의미 없는 값으로 설정되기 때문이다.
- columnDefinition(DDL)
varchar(100) default ‘EMPTY'
와 같이 DB 컬럼 정보를 직접 줄 수 있다.- 기본 값 - 필드의 자바 타입과 방언 정보를 사용해
- length(DDL)
- 문자 길이 제약조건, String 타입에만 사용한다. 서 적절한 컬럼 타입
- 기본 값 - 255
- precision, scale(DDL)
BigInteger
이나BigDecimal
와 같이 범위가 큰 숫자나 소수점 타입에서 사용한다.precision
은 소수점을 포함한 전체 자릿수를,scale
은 소수의 자릿수다.double
,float
타입에는 적용되지 않고, 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용한다.- 기본 값 - precision=19, scale=2
@Enumerated
@Enumerated
enum
타입을 매핑한다.enum
을 DB에 어떤 형식으로 저장할지를 정한다.EnumType
의enum
중 선택한 값으로 매핑한다.
속성
- EnumType.ORDINAL
enum
순서를 DB에 저장한다.- 기본값으로 설정되어 있는데,
enum
에 값이 수정되면서 순서가 바뀌면 이전 데이터가 잘못된 데이터가 되기 때문에 사용하지 않는걸 권장한다.- 기본값으로 설정되어 있는데,
- EnumType.STRING
enum
이름을 DB에 저장한다.
@Temporal
@Temporal
java.util.Date
,java.util.Calendar
를 사용한 날짜 타입을 매핑한다.- 날짜 타입이 DB에는 기본적으로 3가지인데, 그 중 어떤 것으로 매핑할지를 정한다.
TemporalType
의enum
중 선택한 값으로 매핑한다.- 만약
LocalDate
나LocalDateTime
을 사용시, 지정하지 않아도LocalDate
는 date,LocalDateTime
는timestamp
로 변한된다.- 날짜 타입이 DB에는 기본적으로 3가지인데, 그 중 어떤 것으로 매핑할지를 정한다.
속성
- TemporalType.DATE
- 날짜, DB date 타입과 매핑한다.
- ex. 2013–10–11
- TemporalType.TIME
- 시간, DB time 타입과 매핑한다.
- ex. 11:11:11
- TemporalType.TIMESTAMP
- 날짜와 시간, DB
timestamp
타입과 매핑한다.- ex. 2013–10–11 11:11:11
@Lob
@Lob
BLOB
이나CLOB
으로 매핑한다.- 속성을 따로 지정할 수 없고,
필드의 데이터 타입에 따라 자동으로 매핑
된다.CLOB
-String
,char[]
,java.sql.CLOB
BLOB
-byte[]
,java.sql. BLOB
- 속성을 따로 지정할 수 없고,
BLOB
이나CLOB
BLOB
- ?
CLOB
- ?
@Transient
@Transient
- 특정 필드를 컬럼에 매핑하지 않는다.
- 단순히 application에서 값을 저장하거나, 계산하는 용도로 사용하고 싶을 때 사용한다.
기본 키 매핑
매핑 소개
@Id
: 필드가 pk임을 매핑한다.@GeneratedValue
: pk의 값을 자동 생성된 값(시퀀스, 키 생성용 테이블 사용 등)을 사용한다.
@Id
@Id
- 필드가 pk임을 매핑한다.
@GeneratedValue
@GeneratedValue
- @Id에 지정된 pk 컬럼에 넣을 값을 자동으로 생성해주는 전략에 대한 내용을 지정한다.
- 만약 사용하지 않는다면 id값을 직접 세팅해줘야한다.
속성
strategy
에 GenerationType
중 하나를 지정한다.
- GenerationType.IDENTITY
- 기본키의 생성을 DB에 위임한다.
- MYSQL에서
auto_increament
와 같은 기능 사용시 선택한다.- MySQL, PostgreSQL, SQL Server, DB2 사용된다.
- MYSQL에서
특징
auto_increament
의 기능은, DB에insert query
가 실행되어야 id 값을 알 수 있다. 그런데 JPA는 영속성 관리시 id를 가지고 관리하고, 트랜잭션 커밋 시점에insert query
를 실행한다. 그렇기 때문에 이 방식을 사용시, 트랜잭션 커밋 시점에insert query
를 실행하는게 아니라persist()
를 먼저 호출해insert query
를 보내 값을 넣고, id값을 조회해 세팅하며 id를 기준으로 관리한다.
- GenerationType.SEQUENCE
- DB 시퀀스 오브젝트 사용한다.
- 시퀀스에 대한 내용을 지정하고 싶으면,
@SequenceGenerator
을 이용해 지정하면 된다.- ORACLE, PostgreSQL, DB2, H2 에서 사용된다.
- 시퀀스에 대한 내용을 지정하고 싶으면,
@SequenceGenerator
의 속성
- name: 식별자 생성기 이름을 지정한다.
- sequenceName: DB에 등록되어 있는 시퀀스 이름을 지정한다.
- initialValue: DDL 생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 1 시작하는 수를 지정한다.
- allocationSize: 시퀀스 한 번 호출에 증가하는 수로 성능 최적화에 사용된다. DB 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다. 기본값 50
- catalog: DB catalog 이름을 지정한다.
- schema: DB schema 이름을 지정한다.
- GenerationType.TABLE
- 키 생성용 테이블 사용해 DB 시퀀스 기능을 흉내내는 전략이다.
- DB마다
auto_increment
나시퀀스
를 선택해야하는데, 이 전략을 이용하면 모든 DB에서 사용할 수 있다.- 단 따로 테이블을 구성한다고 해도 table lock의 가능성, 최적화 안되어있음 등의 이유로 성능이 조금 떨어질 수 있어 권장되지 않는 전략이다.
@TableGenerator
을 이용해 지정하면 된다. - DB마다
@TableGenerator
의 속성
- name: 식별자 생성기 이름을 지정한다.
- table: 키생성 테이블 이름을 지정한다.
- pkColumnName: 시퀀스 컬럼명을 지정한다.
- valueColumnNa: 시퀀스 값 컬럼명을 지정한다.
- pkColumnValue: 키로 사용할 값 이름을 지정한다.
- initialValue: 초기 값, 마지막으로 생성된 값이 기준이다.
- allocationSize : 시퀀스 한 번 호출에 증가하는 수로 성능 최적화에 사용된다. 기본값 50
- catalog: DB catalog 이름을 지정한다.
- schema: DB schema 이름을 지정한다.
- uniqueConstraints(DDL): 유니크 제약 조건을 지정할 수 있다.
- GenerationType.AUTO
- 방언에 따라
GenerationType.IDENTITY
,GenerationType.SEQUENCE
,GenerationType.TABLE
중 하나를 자동 지정한다.@GeneratedValue
의 기본값이다.
식별자 전략
어떤 키를 기본키로 해야할까?
기본키의 제약 조건
not null
- 유일
- 변하면 안된다.
기본키의 제약 조건을 만족하면서, 먼 미래(서비스 종료시점 전)에도 사용할 수 있는 자연키는 찾기 어렵다. 그래서 대리키(대체키)로 Generate value이나 랜덤값과 같은 비즈니스와 전혀 상관없는 키를 사용하는 것이 좋다. 권장하는 방법은 아래의 내용을 조합해서 사용하는 것을 권장한다.
- Long형: 10억 이상도 처리가 가능하다. 같은 정수형이라도, 데이터의 수가 10억쯤 되면 변경시 어려움이 있을 수 있다.
- 대체키: 자연키가 아닌 대체키
- 키 생성전략: UUID나 랜덤 값을 조합한 회사 내의 룰을 이용해 생성 전략을 이용
- 자연키
- 비즈니스적으로 의미를 가지고 있는 키를 의미한다.
예시로는 주민등록번호, 핸드폰번호와 같은 내용이 있다.
GenerationType.SEQUENCE
와 GenerationType.TABLE
에서 allocationSize
를 이용한 최적화
동작 JPA는 영속성 관리시 id를 가지고 관리하고, 트랜잭션 커밋 시점에 insert query
를 실행한다. id는 DB의 시퀀스 값으로 사용하게 되어있으니, persist()
를 호출시 DB에서 시퀀스의 값을 조회해 세팅하고 그 값으로 관리한다. 이렇게 되면 persist()
호출시마다 DB에 접근하게 된다. 이를 최적화 하는 방법으로 allocationSize
를 이용할 수 있다.
allocationSize
이용시 DB에서 시퀀스 값을 한번에 1개만 조회하는게 아니라, allocationSize
에 지정된 수 만큼 application 메모리에 가져온다. 그러고 persist()
호출시 메모리에 있는 시퀀스 값에서 하나씩 사용하고, 다 사용하면 다시 allocationSize
만큼 시퀀스의 값을 조회한다. 여러대의 웹서버가 있다고 해도 각자 호출시마다 받는 시퀀스의 값이 다르고, 그 사이 내에서 사용하기 때문에 동시성문제가 생기지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
User user1 = new User();
user1.setName("A");
User user2 = new User();
user2.setName("B");
User user4 = new User();
user3.setName("C");
em.persist(user1);
em.persist(user2);
em.persist(user3);
- 10 DB에서 시퀀스 값 조회
- application 실행 이후, 첫
persist()
호출에는 에는 2번 DB에서 조회하는데,allocationSize
을 50으로 지정했다면 시퀀스의 현재값을 -49로 지정하도록 설정하기 때문이다. 그래서 1번 호출시 1이되어 시퀀스의 값이 1부터 시작할 수 있도록 한다. jpa는 1로 1개만 리턴이 오면 한번더 조회해 1~50의 값을 가져오고, DB시퀀스는 51이 된다.
- 11 메모리에서 시퀀스 값 조회
- 12 메모리에서 시퀀스 값 조회
유의점 메모리에 들고 있던 값은 당연히, application 종료시 사라지기 때문에 너무 큰 값을 설정해 중간 값을 날릴 수 있다. 그래서 50~100개 사이로 지정하는 것이 좋다.