본문 바로가기
Develop/JPA

[JPA/기본편] 엔티티 매핑

by J-rain 2024. 3. 20.

 

엔티티 매핑

JPA에서 꼭 알아야 할 2가지는 JPA의 동작 원리를 이해하고 객체와 관계형 데이터베이스를 정확히 매핑하는 것이다.

 

@Entity

역할: @Entity가 붙은 클래스는 테이블과 매핑되고 JPA가 관리한다.

속성: @Entity에 사용할 수 있는 속성으로 name이 있다.

 

name

JPA에서 사용할 엔티티 이름을 지정한다.

사용 코드

@Entity(name = "myUser")
public User { ...}

 

기본 값은 클래스 이름을 그대로 사용한다.

활용 예

다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충돌을 피할수 있다.

주의사항

@Entity 적용 시 주의사항은 아래와 같다.

  • 기본 생성자가 꼭 존재해야한다. (파라미터가 없는 public 또는 protected 생성자)
  • final 클래스, enum, interface, inner 클래스에 사용할 수 없다.
  • 저장할 필드에 final을 사용하면 안 된다.

 

@Table

역할: 엔티티와 매핑할 테이블을 지정한다.

생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.

속성: @Table에서 사용할 수 있는 속성 4가지에 대해서 알아보자.

  • name:  매핑할 테이블 이름을 지정한다.
  • catalog:  데이터베이스에서 catalog를 매핑한다.
  • schema:  데이터베이스에서 schema를 매핑한다.
  • uniqueConstraints:  DDL 생성 시 유니크 제약조건을 만들고 2개 이상의 복합 유니크 제약조건도 만들 수 있다.사용 코드 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다. (이미 만들어진 테이블에 유니크 제약조건을 추가해주진 않는다.)
@Entity
@Table(
    name = "tn_user",
    uniqueConstraints = {
        @UniqueConstraint(columnNames = {"name"})
    })
public user {...}

 

 

 

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

JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다.

스키마 자동 생성 기능을 사용하면 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다.

스키마 자동 생성 기능을 적용하려면 아래 코드를 persistence.xml의 property 부분에 추가하자.

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

위 코드의 value 값으로 사용할 수 있는 속성은 아래와 같다.

  • create
  • 기존 테이블을 삭제하고 새로 생성한다. DROP + CREATE
  • create-drop
  • create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다. DROP + CREATE + DROP
  • update(column 삭제는 반영하지 않는다.)
  • 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다.
  • validate
  • 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다.
  • none
  • 자동 생성 기능을 사용하지 않는다. (자동 생성 기능을 사용하지 않는 또 다른 방법은  <property name="hibernate.hbm2ddl.auto" value="..."/>  속성 자체를 삭제하면 된다.

주의사항!

운영 서버에서 create, create-drop, update 처럼 DDL을 수정하는 옵션은 절대 사용하면 안 된다. (애시당초 DB 계정을 분리시켜야한다.)

개발 환경에 따른 추천 전략은 다음과 같다.

개발 초기 단계는 create 또는 update

초기화 상태로 자동화된 테스트를 진행하는 개발자 환경과 CI 서버는 create 또는 create-drop

테스트 서버는 update 또는 validate

스테이징과 운영 서버는 validate 또는 none

 

 

필드와 컬럼 매핑

@Entity와 @Table을 이용해 클래스를 매핑해봤다.

이번에는 필드와 컬럼 매핑을 알아보자.

아래 그림은 다양한 자료형을 사용한 코드와 그 결과이다.

@Entity
public class Member {

    @Id
    private Long id;

    @Column(name = "name")
    private String username;

    private Integer age;

    @Enumerated(EnumType.STRING)
    private RoleType roleType;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;

    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;

    @Lob
    private String description;

    public Member() {

    }


}

 

 

@Column

@Column은 필드와 컬럼에 더 섬세한 설정을 할 때 사용할 수 있는 속성들이 있다.

@Column 속성은 2가지로 나눌 수 있다.

  1. 애플리케이션 로직에 영향을 주는 속성
  2. name, insertable, updatable, table
  3. DDL 생성 기능에 영향을 주는 속성
  4. nullable, unique, columnDefinition, length, precision, scale

각 항목의 기능은 아래에 작성하였다.

종류 기능 기본값
 name 필드와 매핑할 테이블의 컬럼 이름 객체의 필드 이름  
 insertable,  updatable 등록, 변경 가능 여부 TRUE
 nullable(DDL) null 값의 허용 여부를 설정한다. false로 설정하면 DDL 생성 시에 not null 제약조건이 붙는다.  
 unique(DDL) @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다.  
 columnDefinition(DDL) 데이터베이스 컬럼 정보를 직접 줄 수 있다.
ex) varchar(100) default ‘EMPTY'
필드의 자바 타입과 방언 정보를 사용해서 적절한 컬럼 타입을 매핑
 length(DDL) 문자 길이 제약조건, String 타입에만 사용한다. 255
 precision, scale(DDL) BigDecimal 타입에서 사용한다(BigInteger도 사용할 수 있다).
precision은 소수점을 포함한 전체 자릿수를 설정한다.
scale은 소수의 자릿수를 지정한다.
참고로 double, float 타입에는 적용되지 않는다.
아주 큰 숫자나 정밀한 소수를 다루어야 할 경우만 사용한다.
precision=19, scale=2

 

 

 

@Enumerated

자바 enum 타입을 매핑할 때 사용

종류 기능 기본값
 EnumType.STRING enum 이름을 데이터베이스에 저장 EnumType.ORDINAL
 EnumType.ORDINAL enum 순서를 데이터베이스에 저장  

 @Enumerated 는 STRINGORDINAL 이렇게 2가지가 있다.

 

enum type 활용의 참고하면 좋은 글

 

@Temporal

날짜 타입(java.util.Data, java.util.Calendar) 매핑할 때 사용

종류 기능
TemporalType.DATE 날짜, 데이터베이스 date 타입과 매핑 ex) 2024-01-23
TemporalType.TIME 시간, 데이터베이스 time 타입과 매핑 ex) 14:36:20
TemporalType.TIMESTAMP 날짜와 시간, 데이터베이스 timestamp 타입과 매핑 ( 2024-01-23 14:36:20 )

참고로 최신 하이버네티트 지원으로 LocalDate, LocalDateTime 을 사용하므로 잘 안쓰인다.

 

 

@LOB

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

  • @Lob에는지정할 수있는 속성이 없다.
  • 매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB매핑
    • CLOB : String, char[ ], java.sql.CLOB
    • BLOB : byte[ ], java.sql.BLOB

 

@Transient

필드 매핑 X

데이터 베이스에 저장X, 조회X

주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용

ex) @Transient
    private Integer temp;

 

 

 


기본 키 매핑

 

기본 키 매핑 어노테이션

  • @Id
  • @GenereatedValue

JPA가 제공하는 데이터베이스 기본 키 생성 전략은 다음과 같다.

  • 직접 할당
  • 기본 키를 애플리케이션에서 직접 할당한다.
  • 자동 생성
    • IDENTITY: 기본 키 생성을 데이터베이스에 위임한다.
    • SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
    • TABLE: 키 생성 테이블을 사용한다.
  • 대리 키 사용 방식으로 3가지 전략이 있다.

자동 생성 전략이 다양한 이유!

데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다.

오라클 데이터베이스 : 시퀀스 제공

MySQL : AUTO_INCREMENT 제공

 

IDENTITY 전략

@Entity
public class Board {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
}
private static void logic(EntityManager em) {
  Board board = new Board();
  em.persist(board);
  System.out.println("board.id = " + board.getId());
}
// 출력 : board.id = 1

 

IDENTITY 전략과 최적화

IDENTITY 전략은 데이터를 데이터베이스 INSERT한 후에 기본 키 값을 조회할 수 있다.

따라서 엔티티에 식별자 값을 할당하려면 JPA는 추가로 데이터베이스를 조회해야 한다.

JDBC3에 추가된 Statement.getGenratedKeys()를 사용하면 데이터를 저장하면서 동시에 생성된 키 값도 얻어 올 수 있다.

하이버네이트는 이 메소드를 사용해서 데이터베이스와 한 번만 통신한다.

IDENETITY 전략은 쓰기 지연 불가

엔티티가 영속 상태가 되려면 식별자가 반드시 필요하다.

그런데 IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다.

따라서 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.

 

 

SEQUENCE 전략

@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;
  
}

@SequenceGenerator(...)를 통해서 시퀀스 생성기를 등록한다.

그리고 @GeneratedValue()에 키 생성 전략을  GeneratedType.SEQUENCE  로 설정하고

 generator = SEQUENCE_NAME  을 통해 시퀀스 생성기를 할당한다.

private static void logic(EntityManager em) {
  Board board = new Board();
  em.persist(board);
  System.out.println("board.id = " + board.getId());
}
// 출력 : board.id = 1

 

@SequenceGenerator 속성 정리

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

 

SEQUNCE 전략과 최적화

SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요하다.

따라서 데이터베이스와 2번 통신한다.

  1. 식별자를 구하려고 데이터베이스 시퀀스를 조회한다.
  2. 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장한다.

JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator.allocationSize를 사용한다.

allocationSize 값만큼 시퀀스를 증가시키고 애플리케이션 메모리에서 식별자를 할당한다.

예를들면 allocationSize 값이 10이면 시퀀스를 한 번에 10 증가시킨 다음에 1~10까지는 메모리에서 식별자를 할당한다.

IDENTITY 방식과 SEQUENCE 방식의 차이

SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다.

그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다.

이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장한다.

IDENTITY 방식은 엔티티를 데이터베이스에 저장한 후 식별자를 조회해서 엔티티의 식별자에 할당한다.

 

 

TABLE 전략

TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스와 비슷한 전략이다.

이 방식은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다.

@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;
  ...
}

@TableGenerator를 사용해서 테이블 키 생성기를 등록한다.

그리고 @GeneratedValue에서 키 생성 전략을 TABLE 방식으로 지정하고 생성기를 등록해준다.

private static void logic(EntityManager em) {
  Board board = new Board();
  em.persist(board);
  System.out.println("board.id = " + board.getId());
}
// 출력 : board.id = 1

 

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

 

Table 전략과 최적화

TABLE 전략은 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용한다. 이 전략은 SEQUENCE 전략과 비교해서 데이터베이스와 한 번 더 통신하는 단점이 있다.

TABLE 전략을 최적화하려면 SEQUENCE 방식처럼 allocationSize를 지정하면 된다.

 

권장하는 식별자 전략

식별자는 어떤 값으로 사용해야될까

데이터베이스 키 조건

일단 데이터베이스의 기본 키 조건을 확인해보자.

  • null 값은 허용하지 않는다.
  • 유일해야 한다.
  • 변해선 안 된다.

키 선택 전략

테이블의 기본 키를 선택하는 전략은 크게 2가지가 있다.

  • 자연 키(natural key)예 : 주민등록번호, 이메일, 전화번호
  • 비즈니스에 의미가 있는 키
  • 대리 키= 대체 키(surrogate key)예 : 시퀀스, auto_increment, 키 생성 테이블
  • 비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불림

결론은 자연 키보다는 대리 키(대체 키)를 권장한다.

비즈니스 환경은 언젠가 변할 수 있기 때문에 모든 엔티티에 일관된 방식으로 대리 키 사용을 권장한다.

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

[JPA/기본편] 연관관계 매핑  (0) 2024.03.20
[JPA/기본편] 연관관계  (1) 2024.03.20
[JPA/기본편] 준영속 상태  (0) 2024.03.20
[JPA/기본편] 플러시  (0) 2024.03.20
[JPA/기본편] 영속성 컨텍스트  (0) 2024.03.20

댓글