Post

Springboot Jpa - 엔티티 매핑

엔티티를 매핑하는 방법을 알아보자.

Springboot Jpa - 엔티티 매핑

객체와 테이블 매핑

매핑 소개

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

@Entity

@Entity
JPA가 관리하는 엔티티가 된다.
JPA를 사용해 테이블과 매핑할 클래스는 필수적으로 붙여줘야함
접근 제어자가 public, protected 중 하나인 기본 생성자가 필수적으로 있어야한다. JPA의 기본 스펙이기도 하지만, 리플렉션이나 프록시등에서 다뤄야하기 떄문이기도 하다.
final, enum, interface, inner 클래스에서는 사용할 수 없다.
저장할 필드에 final을 사용할 수 없다.

속성

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
unique(DDL)
@TableuniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다.
잘 사용되지 않는 이유는 생성된 unique key의 이름이 랜덤으로 의미 없는 값으로 설정되기 때문이다.
@TableuniqueConstraints는 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에 어떤 형식으로 저장할지를 정한다.
EnumTypeenum 중 선택한 값으로 매핑한다.

속성

EnumType.ORDINAL
enum 순서를 DB에 저장한다.
기본값으로 설정되어 있는데, enum에 값이 수정되면서 순서가 바뀌면 이전 데이터가 잘못된 데이터가 되기 때문에 사용하지 않는걸 권장한다.
EnumType.STRING
enum 이름을 DB에 저장한다.

@Temporal

@Temporal
java.util.Date, java.util.Calendar를 사용한 날짜 타입을 매핑한다.
날짜 타입이 DB에는 기본적으로 3가지인데, 그 중 어떤 것으로 매핑할지를 정한다.
TemporalTypeenum 중 선택한 값으로 매핑한다.
만약 LocalDateLocalDateTime을 사용시, 지정하지 않아도 LocalDate는 date, LocalDateTimetimestamp로 변한된다.

속성

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값을 직접 세팅해줘야한다.

속성

strategyGenerationType 중 하나를 지정한다.

GenerationType.IDENTITY
기본키의 생성을 DB에 위임한다.
MYSQL에서 auto_increament와 같은 기능 사용시 선택한다.
MySQL, PostgreSQL, SQL Server, DB2 사용된다.

특징 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을 이용해 지정하면 된다.

@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.SEQUENCEGenerationType.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개 사이로 지정하는 것이 좋다.

This post is licensed under CC BY 4.0 by the author.