nyximos.log

[JPA] 엔티티 매핑 본문

Programming/JPA

[JPA] 엔티티 매핑

nyximos 2022. 2. 25. 17:18

 

자바 ORM 표준 JPA 프로그래밍

김영한

 

 

 

🚵‍♀️ @Entity 

JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야 한다.

 

@Entity 적용 시 주의사항

1. 기본 생성자는 필수 (파라미터가 없는 public 또는 protected 생성자)
     → 생성자가 없으면 자바는 기본 생성자를 자동으로 만든다.

2. final 클래스, enum, interface, inner 클래스에는 사용할 수 🙅‍♀️

3. 저장할 필드에 final 을 사용하면 안된다.

 

속성

name

JPA에서 사용할 엔티티 이름을 지정한다. (기본값 : 클래스 이름)

 

 

🧗‍♀️ @Table

엔티티와 매핑할 테이블을 지정한다. (기본값 : 매핑한 엔티티 이름)

 

속성

name

매핑할 테이블 이름

 

catalog

catalog 기능이 있는 데이터베이스에서 catalog 매핑

 

schema

schema 기능이 있는 데이터베이스에서 schema 매핑

 

uniqueConstraints

DDL 생성 시에 유니크 제약조건을 만든다.
2개 이상의 복합 유니크 제약조건도 만들 수 있다.이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다.

 

 

🤽‍♂️ 데이터베이스 스키마 자동 생성

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

클래스의 매핑 정보를 보면 어떤 테이블에 어떤 컬럼을 사용하는지 알 수 있다.

JPA는 이 매핑정보와 데이터베이스 방언을 사용해서 데이터베이스 스키마를 생성한다.

 

persistence.xml에 다음 속성을 추가

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

속성

create

기존 테이블 삭제후 새로 생성 (DROP + CREATE)

 

create-drop

create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL 삭제

DROP + CREATE + DROP

 

update

데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다.

 

validate

데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다.

이 설정은 DDL을 수정하지 않는다.

 

none

자동 생성 기능을 사용하지 않는다.

 

👻 운영 서버에서는 DDL을 수정하는 옵션은 절대 사용하면 안된다.

 

👻 JPA 2.1부터 스키마 자동 생성 기능을 표준으로 지원한다.

     하이버네이트의 hibernate.hbm2ddl.auto 속성이 지원하는 update, validate 옵션을 지원하지 않는다.

 

애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성하는 속성

  • 애플리케이션을 실행하면 콘솔에 DDL이 출력되면서 실제 테이블이 생성된다.
  • 자동 생성되는 DDL은 지정한 데이터베이스 방언에 따라 달라진다.
<property name="hibernate.show_sql" value="true">

 

 

🏋️‍♀️ DDL 생성 기능

DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.

 

 

🏄‍♀️ 기본 키 매핑

JPA가 제공하는 기본 키 생성 전략

직접 할당 : 기본 키를 애플리케이션에서 직접 할당한다.  @Id

자동 할당 : 대리 키 사용방식  @Id @GeneratedValue

  • IDENTITY : 기본 키 생성을 데이터베이스에 위임
  • SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
  • TABLE : 키 생성 테이블을 사용한다. 

👻 자동 생성 전략이 다양한 이유 : 데이터베이스 벤더마다 지원하는 방식이 달라서

 

👻 키 생성 전략 사용시 persistence.xml에 다음 속성을 추가하자.

       true 설정하면 키 생성 성능을 최적화하는 allocationSize 속성을 사용하는 방식이 달라진다.

       기존 하이버네이트 시스템을 유지보수 하는 것이 아니라면 반드시 true로 설정하자.

<property name="hibernate.id.new_generator_mappings" value="true">

 

1. 기본 키 직접 할당 전략

em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당하는 방법이다.

 

@Id로 매핑한다.

@Id
@Column(name = "id)
private String id;

 

@Id 적용 가능 자바타입

  • 자바 기본형
  • 자바 래퍼Wrapper 형
  • String
  • java.util.Date
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger

 

2. IDENTITY 전략

기본 키 생성을 데이터 베이스에 위임

MySQL, PostgreSQL, SQL Server, DB2에서 사용

@Id와 @GeneratedValue(strategy = GenerationType.IDENTITY) 사용

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

 

👻 IDENTITY 전략은 데이터를 데이터베이스에 INSERT한 후에 조회할 수 있다.

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

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

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

 

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

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

       → em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다.

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

 

3. SEQUENCE 전략

데이터베이스 시퀀스 : 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트

SEQUENCE 전략은 이 시퀀스를 사용해 기본키를 생성한다.

Oracle, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.

 

1. 시퀀스 생성

CREATE TABLE BOARD(
	ID BIGINT NOT NULL PRIMARY KEY,
    DATA VARCHAR(255)
)

CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;

 

2. @SequenceGenerator를 사용해 시퀀스 생성기 등록

     속성은 아래에 정리했다.

 

3. 데이터베이스 시퀀스 매핑

    @Id와 @GeneratedValue 사용

    BOARD_SEQ_GENERATOR 시퀀스 생성기가 식별자 값을 할당한다.

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

 

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

       → 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장

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

 

@SequenceGenerator 속성 (기본값)

name

식별자 생성기 이름 (필수 속성)

 

sequenceName

데이터베이스에 등록되어 있는 시퀀스 이름 (hibernate_sequence)

 

initialValue

DDL 생성 시에만 사용, 시퀀스 DDL을 생성할 때 처음 시작하는 수 (1)

 

allocationSize

시퀀스 한 번 호출에 증가하는 수, 성능 최적화에 사용 (50)

시퀀스를 호출할 때마다 값이 50씩 증가한다.

시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 1로 설정하자

 

catalog, schema

데이터베이스 catalog, schema 이름

 

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

       1. 식별자를 구하려고 데이터베이스 시퀀스를 조회한다.

       2. 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장한다.

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

      속성 값이 50이면 시퀀스를 한번에 50 증가 시킨후 1~50까지 메모리에서 식별자를 할당한다.

      51이되면 시퀀스 값을 100으로 증가시킨 후 51~100까지 메모리에서 식별자를 할당한다.

 

      시퀀스 값을 선점하므로 여러 JVM이 동시에 동작해도 기본 키 값이 충돌하지 않는다.

      반면 데이터베이스에 직접 접근해서 데이터를 등록할 때 시퀀스 값이 한번에 많이 증가하는 점을 염두하자.

      INSERT 성능이 중요하지 않으면 속성값을 1로 사용하자.

 

4. TABLE 전략

키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략

모든 데이터베이스에 적용 가능

SEQUENCE 전략과 내부 동작방식이 같다.

 

1. 키 생성 용도로 사용할 테이블 생성

    sequence_name : 시퀀스명 , next_val : 시퀀스 값

CREATE TABLE MY_SEQUENCES(
    sequence_name varchar(255) not null,
    next_val bigint,
    primary key (sequence_name)
)

2. @TableGenerator 사용하여 테이블 키 생성기 등록

     속성은 아래에 정리했다.

 

3. 키 생성용 테이블 매핑

     @Id와 @GeneratedValue 사용

     @GeneratedValue.generator에 방금 만든 테이블 키 생성기 지정

@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 속성 (기본값)

name

식별자 생성기 이름 (필수 속성)

 

table

키생성 테이블명 (hibernate_sequences)

 

pkColumnName

시퀀스 컬럼명 (sequence_name)

 

valueColumnName

시퀀스 값 컬럼명(next_val)

 

pkColumnValue

키로 사용할 값 이름 (엔티티 이름)

 

initialValue

초기 값, 마지막으로 생성된 값이 기준 (0)

 

allocationSize

시퀀스 한 번 호출에 증가하는 수, 성능 최적화에 사용 (50)

 

catalog, schema

데이터베이스 catalog, schema 이름

 

uniqueConstraints(DDL)

유니크 제약 조건 지정

 

 

👻 TABLE 전략은 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용한다.

       이 전략은 SEQUENCE 전략과 비교해서 데이터베이스와 한 번 더 통신하는 단점이 있다.

       최적화 하는 방법은 SEQUENCE 전략과 같다.

 

5. AUTO 전략

데이터 베이스 방언에 따라 자동으로 선택한다.

ORACLE = SEQUENCE

MYSQL = IDENTITY

 

데이터베이스를 변경해도 코드를 수정할 필요가 없다.

키 생성 전략이 아직 확정되지 않은 개발 초기 단계, 프로토타입 개발시 용이하다.

만약 SQUENCE, TABLE 전략이 선택될 시 시퀀스와 키 생성용 테이블을 미리 만들어 두어야 한다.

스키마 자동 생성 기능을 사용하면 하이버네이트가 적절히 만들어준다.

 

🏂 기본키 매핑 정리 

영속성 컨텍스트는 엔티티를 식별자 값으로 구분

→ 엔티티를 영속 상태로 만들려면 식별자 값이 필요하다.

 

em.persist() 호출 직후 발생하는 일

  • 직접 할당 : em.persis() 호출 전 애플리케이션에서 직접 식별자 값을 할당한다. (식별자 값이 없을 경우 예외 발생)
  • SEQUENCE : 데이터베이스 시퀀스에서 식별자 값을 획득 → 영속성 컨텍스트에 저장
  • TABLE : 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득 → 영속성 컨텍스트에 저장 
  • IDENTITY : 데이터베이스에 엔티티를 저장해서 식별자 값 획득 → 영속성 컨텍스트에 저장
                          (테이블에 데이터 저장해야 값을 획득할 수 있다.)

 

권장하는 식별자 선택 전략

데이터 베이스 기본키 조건

1. not null

2. 유일해야 한다.

3. 변해선 안된다.

 

테이블 기본 키 선택 전략

1. 자연 키 (natural key) : 비즈니스에 의미가 있는 키 (주민등록번호, 이메일)

2. 대리 키 (surrogate key) : 비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불린다.

                                                     (오라클 시퀀스, auto_increment, 키생성 테이블 사용)

 

 

👻 자연키보다는 대리키를 권장한다.

       비즈니스 규칙은 생각보다 쉽게 변한다.

 

👻 비즈니스 환경은 언젠가 변한다.

       비즈니스와 관련없는 대리 키를 사용하되, 주민등록 번호나 이메일처럼 자연 키의 후보가 되는 컬럼들은

       유니크 인덱스를 설정해서 사용하는 것을 권장한다.

 

👻 JPA는 모든 엔티티에 일관된 방식으로 대리 키 사용을 권장한다.

        테이블은 한 번 정의하면 변경하기 어렵다.

 

 

🚣‍♀️ 필드와 컬럼 매핑: 래퍼런스

@Column

컬럼을 매핑한다.

 

속성(기본값)

name

필드와 매핑할 테이블의 컬럼 이름 (객체의 필드 이름)

 

nullable(DDL)

null 값의 허용 여부 설정 (true)

false로 설정시 not null 제약 조건이 붙는다.

false로 지정하는 것이 안전하다.

 

length(DDL)

문자 길이 제약조건, String 타입에만 사용 (255)

 

unique(DDL)

@Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약 조건을 걸 때 사용

클래스 레벨에서 @Table.uniqueConstraints 사용 : 두 컬럼 이상을 사용해 유니크 제약 조건을 사용시

 

columnDefinition(DDL)

데이터베이스 컬럼 정보를 직접 줄 수 있다. (필드의 자바 타입과 방언 정보를 사용해서 적절한 컬럼 타입 생성)

 

precision, scale(DDL)

BigDecimal, BigInteger 타입에서 사용 (pricision=19, scale=2)

precision : 소수점을 포함한 전체 자릿수

scale : 소수의 자릿수

double, float 타입에는 적용되지 않는다.

아주 큰 숫자나 정밀한 소수를 다룰 경우만 사용하자.

 

insertable

엔티티 저장 시 이 필드도 같이 저장한다. (true)

false로 설정하면 이 필드는 데이터베이스에 저장하지 않는다.

false 옵션은 읽기 전용일 때 사용한다.

 

updatable

엔티티 수정 시 이 필드도 같이 저장한다.

false로 설정하면 이 필드는 데이터베이스에 수정하지 않는다.

false 옵션은 읽기 전용일 때 사용한다.

 

table

하나의 엔티티를 두 개 이상 테이블에 매핑할 때 사용한다.(현재 클래스가 매핑된 테이블)

지정한 필드를 다른 테이블에 매핑할 수 있다.

 

@Enumerated

자바의 enum 타입을 매핑한다.

 

속성

value

EnumType.ORDINAL : enum 순서를 데이터베이스에 저장 (기본값)

EnumType.STRING : enum 이름을 데이터베이스에 저장 (권장)

 

👻 저장된 enum의 순서가 바뀌거나 추가되어도 안전한 EnumType.STRING을 사용하도록 하자.

 

@Temporal

날짜 타입을 매핑한다.

생략시 자바의 Date와 가장 유사한 timestamp로 정의된다.

datetime : MySQL

timestamp : H2, Oracle, PostgreSQL

 

속성

value(필수 지정 )

TemporalType.DATE : 날짜, 데이터베이스 date 타입과 매핑 (2021-02-25)

TemporalType.TIME : 시간, 데이터베이스 time 타입과 매핑 (11:22:33)

TemporalType.TIMESTAMP : 날짜, 시간, 데이터베이스 timestamp 타입과 매핑 (2021-02-25 11:22:33)

 

@Lob

BLOB, CLOB 타입을 매핑한다.

 

CLOB : String, char[], java.sql.CLOB

BLOB : byte[], java.sql.BLOB

 

@Transient

특정 필드를 데이터베이스에 매핑하지 않는다.

데이터베이스에 저장/조회 🙅‍♀️

객체에 임시로 어떤 값을 보관하고 싶을 때

 

@Access

JPA가 엔티티에 접근하는 방식을 지정한다.

AccessType.FIELD : 필드 직접 접근, private이어도 접근 🙆‍♀️

AccessType.PROPERTY : 접근자Getter를 사용

 

 

 

참조

김영한,  자바 ORM 표준 JPA 프로그래밍, 에이콘출판주식회사, 2015

자바 ORM 표준 JPA 프로그래밍 - 기본편

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

 

 

 

 

 

 

 

 

 

 

참조

김영한,  자바 ORM 표준 JPA 프로그래밍, 에이콘출판주식회사, 2015

자바 ORM 표준 JPA 프로그래밍 - 기본편

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com