엔티티 매핑

이상훈·2022년 10월 9일
0

Jpa

목록 보기
2/17

김영한님의 인프런 강의 '자바 ORM 표준 JPA 프로그래밍'을 참고했습니다.

객체와 테이블 매핑

@Entity

  • @Entity가 붙은 클래스는 JPA가 관리한다. 따라서 JPA를 사용해서 테이블과 매핑할 클래스에는 @Entity를 꼭 붙여야 한다.

  • 기본 생성자가 필수이다.
    case 1 : 생성자를 생략할 경우 -> 자바는 생성자가 하나도 없으면 기본 생성자를 자동으로 만들어준다.
    case 2 : 생성자가 1개라도 있으면 -> 기본 생성자를 직접 만들어야 한다.


속성기능기본값
nameJPA에서 사용할 엔티티 이름을 지정한다.클래스 이름을 그대로 사용한다.

다른 패키지에 같은 클래스 이름이 없으면 가급적 기본값을 사용한다.


@Table

  • @Table은 엔티티와 매핑할 테이블을 지정한다.

속성기능기본값
name매핑할 테이블 이름.엔티티 이름을 사용한다.
catalogcatalog 기능이 있는 데이터베이스에서 catalog를 매핑한다.
schemaschema 기능이 있는 데이터베이스에서 schema를 매핑한다.
uniqueConstraints(DDL)DDL 생성 시에 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 참고로 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다.

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

지금까지는 테이블을 먼저 생성하고 그 다음에 엔티티를 만들었지만 데이터베이스 스키마 자동 생성을 사용하면 엔티티만 만들면 테이블을 자동으로 생성할 수 있다.
예를 들어 다음과 같이 엔티티를 만들었을 뿐인데

그에 맞는 DDL이 자동으로 만들어짐을 확인할 수 있다.
(hibernate.show_sql 속성을 true로 설정하면 콘솔에 실행되는 테이블 생성 DDL을 출력할 수 있다)

설정만 하면 JPA는 데이터베이스 방언을 참고해서 데이터베이스에 맞는 적절한 DDL을 생성해 준다.

가변 문자의 경우 oracle은 VARCHAR2, mysql은 VARCHAR이다. persistence.xml에 설정한 방언에 따라 적절한 DDL을 생성해 준다.

데이터베이스 스키마 자동 생성 기능을 사용하려면 persistence.xml에 다음 속성을 추가하면 된다.

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

이 속성을 추가하면 애플리케이션 실행 시점에 기존 테이블을 삭제하고 테이블을 새로 만들어준다. 아래에 속성들을 정리해 봤다.

옵션설명
create기존 테이블을 삭제하고 새로 생성한다.
create-dropcreate와 같으나 종료 시점에 테이블을 drop 한다.
update변경 사항만 수정한다.
validate엔티티와 테이블이 정상 매핑되었는지만 확인한다.
none자동 새성 기능을 사용하지 않는다.

주의사항

개발 환경에 따른 전략

  • 개발 초기 단계 : create 또는 update
  • 테스트 서버 : update 또는 validate
  • 스테이징과 운영 서버 : validate 또는 none

    운영 서버에서 create, create-drop, update처럼 DLL을 수정하는 옵션은 절대로 사용하면 안된다. 복구하기 힘든 심각한 에러가 발생한다.


필드와 컬럼 매핑

스키마 자동 생성하기를 통해 만들어지는 DDL에 다양한 제약 조건을 추가할 수 있다. 이러한 기능들은 단지 DDL을 자동 생성할 때만 사용되고 JPA 실행 로직에는 영향을 주지 않는다. 그러면 만약 DDL을 직접 만든다면 사용할 이유가 없을까?

답은 아니다. 이 기능을 사용하면 애플리케이션 개발자가 엔티티만 보고도 손쉽게 다양한 제약 조건을 파악할 수 있는 장점이 있다.


@Column

@Column은 객체 필드를 테이블 컬럼에 매핑한다. 가장 많이 사용되고 기능도 많다.

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

속성 중에 주로 name, nullable이 사용되고 나머지는 잘 사용되지 않는다.


@Enumerated

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

속성설명기본값
value• EnumType.ORDINAL: enum 순서를 데이터베이스에 저장
• EnumType.STRING: enum 이름을 데이터베이스에 저장 EnumType.ORDINAL
EnumType.ORDINAL

ORDINAL은 사용하지 말자. 위험하다!!


@Temporal

날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용한다.

속성설명기본값
value• TemporalType.DATE: 날짜, 데이터베이스 date 타입과 매핑
• TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑
• TemporalType.TIMESTAMP: 날짜와 시간, 데이터베이 스 timestamp 타입과 매핑

@Temporal은 요새는 잘 사용 안함. 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; 

기본 키 매핑

기본 키

데이터베이스 기본 키는 다음 3가지 조건을 모두 만족해야 한다.

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

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

  • 자연 키
    • 비즈니스에 의미가 있는 키
    • ex) 주민등록번호, 이메일, 전화번호
  • 대리키
    • 비즈니스와 관련 없는 임의로 만들어진ㄴ 키, 대체 키로도 불린다.
      ex) 오라클 시퀀스, auto_increment, 키생성 테이블 사용

자연 키보다는 대리키를 사용하자!!

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

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

직접 할당

기본 키를 직접 할당하려면 @ID만 사용하면 된다.

@Entity
public class Member {
    
    @Id
    private String id;
    
    @Column(name = "name")
    private String username;

    //getter setter
    ...

직접 할당은 em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당한다. 할당하지 않으면 예외가 발생한다.

			.
            .
Member member = new Member();
member.setId("member1");	// 기본 키를 직접 할당한다.
member.setUsername("a");

em.persist(member);

tx.commit; 	//트랜잭션 커밋
            .
            .
  • 생성된 DDL

IDENTITY 전략

IDENTITY 전략은 기본 키 생성을 데이터베이스에 위임한다. 주로 MySQL에서 사용한다. IDENTITY 전략에서는 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다는 작은 단점이 있다.

  • 🤔 WHY?
    JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL을 시행한다. auto_increment는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있다. 하지만 영속성 컨텍스트는 엔티티를 관리할때 PK 값을 필요로 하는데 여기서 모순이 생긴다. 따라서 IDENTITY 전략은 em.persist() 시점에 즉시 INSERT SQL을 실행하고 DB에서 식별자를 조회한다. 따라서 INSERT 쿼리들을 모아서 날릴 수 없다.

IDENTITY 전략을 사용해 기본 키를 생성하고 DDL을 만들어보자.
먼저 persistence.xml에서 방언을 MySQL로 바꾸자.

<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>

@GeneratedValue의 strategy 속성 값을 GenerationType.IDENTITY로 하자. auto_increment는 정수이므로 Long 타입을 사용해야 한다.

@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "name")
    private String username;

    //getter setter
    ...
}

이제 애플리케이션에서 기본 키를 할당안해도 된다.

			.
            .
Member member = new Member();

member.setUsername("a");

em.persist(member);

tx.commit; 	//트랜잭션 커밋
            .
            .

생성된 DDL


SEQUENCE 전략

데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다. SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다. 주로 오라클, H2 데이터베이스에서 사용한다. 사용법은 앞서 IDENTITY 전략과 마찬가지로 방언을 H2나 오라클로 바꾸고 strategy 속성 값을 GenerationType.SEQUENCE로 바꾸면 된다.

아래와 같이 데이터베이스에 시퀀스가 생성되었고 기본키 1이 저장되었음을 확인할 수 있다.


위에서는 따로 시퀀스 이름을 지정해 주지 않아서 기본 시퀀스인 hibernate_sequence가 사용되었다. 테이블마다 시퀀스를 따로 관리하고 싶거나 속성을 추가하려면@SequenceGenerator를 사용해 매핑하면 된다.


@SequenceGenerator 속성

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

  • 성능 최적화
    SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요하다. 따라서 데이터베이스와 두번 통신한다.
      1. 식별자를 구하려고 데이터베이스 시퀀스를 조회한다.
      1. 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장한다.

IDENTITY 전략과 달리 JPA가 pk 값을 내부적으로 데이터베이스에서 영속성 컨텍스트로 가져올 수 있다.

JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 allocationSize를 사용한다. 여기에 설정한 값만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당한다. 예를 들어 allocationSize 값이 50이면 시퀀스를 한 번에 50 증가시킨 다음 1~50까지는 메모리에서 식별자를 할당한다. 그리고 51이 되면 시퀀스 값을 100으로 증가시킨 다음 51~100까지 메모리에서 식별자를 할당한다. 이 최적화 방법은 동시성 이슈와 상관없다는 장점이 있지만 시퀀스 값이 한번에 많이 증가하므로 적절한 allocationSize 값을 설정하는것이 중요하다.


TABLE 전략

TABLE 전략은 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내 내는 전략이다. 모든 데이터베이스에 적용 가능하다는 장점이 있지만 성능이 조금 떨어진다는 단점이 있다. 앞서 SEQUENCE 전략과 마찬가지로 방언과 strategy 속성값을 바꾸고@TableGenerator를 쓰면 된다. TABLE 전략은 시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작 방식이 같다.

@TableGenerator 속성

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

AUTO 전략

AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다. 예를 들어 오라클을 선택하면 SEQUENCE를, MySQL을 선택하면 IDENTITY를 사용한다.

profile
Problem Solving과 기술적 의사결정을 중요시합니다.

0개의 댓글