본문 바로가기
JPA

엔티티 매핑

by 성건희 2020. 4. 6.
반응형

객체와 테이블 매핑 : @Entity, @Table

필드와 컬럼 매핑 : @Column

기본 키 매핑 : @Id

연관관계 매핑 : @ManyToOne, @JoinColumn

 

객체와 테이블 매핑

@Entity

JPA가 관리하는 클래스, 엔티티라고 한다.

JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수

 

주의점

  • 기본 생성자 필수
  • final 클래스, enum, interface, inner 클래스 사용 X
  • DB에 저장할 필드에 final 사용 X

 

@Table

엔티티와 매핑 할 테이블 지정

 

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

DDL을 애플리케이션 실행 시점에 자동 생성해 준다.

필자는 학원에 다닐 때 항상 이것을 이용해서 스키마를 자동 생성했었는데,

사실 이것은 개발 서버에서만 사용해야 한다는 것을 알게 되었다..

운영 서버에서는 생성된 DDL이 불안할 수 있는 요소가 있어서 적절히 다듬은 후에 사용해야 한다고 한다.

 

속성

create : 기존 테이블 삭제 후 다시 생성 (drop + create)

create-drop : create와 같으나 종료 시점에 테이블 삭제

update : 변경 사항만 반영해준다. - 필드 추가 시 테이블은 날리기 싫은데 컬럼 추가만 하고 싶을 때 사용 (지우는건 안됨)

validate : 엔티티와 테이블이 정상 매핑되었는지 확인해 줌 (SchemaManagementException 발생)

none : 기능 자체를 사용안하고 싶을 때

 

운영 서버에서는 절대로 create, create-drop, update를 사용하면 안된다!!!

개발 초기 단계는 create 또는 update를 사용해서 로컬에서 사용

테스트 서버(여러 사람들이 공용으로 쓰는)는 update 또는 validate - 사실 update도 쓰지 않는걸 추천

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

 

update를 쓰지 않는 이유가 뭘까?

실무에서 운영서버에 데이터가 몇 천만 건 있는 상황인데

alter를 반영하면 데이터베이스 락이 걸려 시스템이 중단 상태가 될 수도 있다.

애플리케이션 로딩 시점에 시스템이 자동으로 alter를 반영한다는 것은 굉장히 위험하다.

가급적이면 본인이 직접 테이블을 작성하는 것을 추천한다.

김영한님은 JPA가 생성하는 DDL 쿼리를 복사해서 다듬어 사용하신다고 한다.

 

DDL 생성 기능

제약조건 추가 - @Column

유니크 제약조건 추가 - @Table(uniqueConstraints = {@UniqueConstraint{...}})

 

DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고

JPA의 실행 로직에는 영향을 주지 않는다.

 

필드와 컬럼 매핑

@Column

객체 필드를 테이블 컬럼에 매핑할 떄 사용

  • name : 필드와 매핑 할 테이블 컬럼 명

  • insertable (거의 사용 X) : 엔티티 저장 시 필드도 같이 저장된다. false 설정 시 DB 저장 X

  • updatable (거의 사용 X) : 엔티티 수정 시 필드도 같이 수정된다. false 설정 시 DB 수정 X

  • table (거의 사용 X) : 하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용

  • nullable : null 값 허용 여부 설정

  • unique : 유니크 제약 조건 설정

  • columnDefiniton : 데이터베이스 컬럼 정보를 직접 줄 수 있다.

    @Column(columnDefiniton = "varchar(100) default 'EMPTY'")
    
  • length : 문자 길이 제약조건

  • precision, scale : BigDecimal 타입에서 사용한다. (BigInteger도 가능)

 

@Enumerated

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

  • EnumType.ORDINAL : enum 순서를 데이터베이스에 저장
  • EnumType.STRING : enum 이름을 데이터베이스에 저장

 

ORDINAL 방식은 enum 데이터를 추가할 때 순서가 변경되므로 데이터가 꼬이게 된다.

따라서 절대 사용하면 안된다.

 

@Temporal

자바 8 때 LocalDate, LocalDateTime이 추가되면서 사실상 최근에는 쓸 필요가 없다.

LocalDate -> date

LocalDateTime -> timestamp

 

@Lob

매핑하는 필드 타입이 문자(String, char[], java.sql.CLOB)면 CLOB 매핑

나머지(byte[], java.sql.BLOB)는 BLOB 매핑

 

@Transient

필드 매핑을 하기 싫을 때 사용한다.

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

 

기본 키 매핑

  • @Id
  • @GeneratedValue

 

기본 키 매핑 방법

직접 할당 : @Id

자동 생성 (@GeneratedValue) : 자동 생성의 전략은 4가지 전략이 있다.

 

IDENTITY

기본 키 생성을 데이터베이스에 위임하는 전략이다.

 

이 전략은 실제 DB에 데이터가 들어가야 ID값이 생성된다.

JPA는 트랜젝션이 커밋되는 시점DB에 반영한다.

 

그럼 여기서 의문이 생길 수 있다.

영속성 컨텍스트의 1차 캐시에 데이터를 넣을 때는 ID 값이 필요한데.. 그럼 어떻게 값을 넣음?

그래서 이 전략을 사용하면 예외적으로 em.persist()를 호출할 때 INSERT 쿼리가 나가게 된다.

따라서 버퍼링 기능을 사용하지 못한다는 단점이 있기는 하지만,

버퍼링이 비약적으로 성능에 영향을 주지는 않는다.

 

SEQUENCE

데이터베이스 시퀀스 오브젝트를 사용하는 전략

 

시퀀스 마다 따로 관리하고 싶다면 @SequenceGenerator 사용

 

em.persist()를 호출 할 때, ID값이 필요하므로 다음의 쿼리가 나간다.

Hibernate:
    call next value for MEMBER_SEQ // MEMBER_SEQ의 다음 값을 내놔

시퀀스만 얻어 온 것이므로 버퍼링 기능을 사용할 수 있다.

 

그런데 여기서 의문이 생긴다.

"아니 매번 네트워크를 타서 값을 가져오는건 성능상 이슈가 많을 것 같은데?"

맞는 말이다.

이러한 문제 때문에 SEQUENCE 전략에서는 성능 최적화 기능을 지원한다.

예시를 보자

@Entity
@SequenceGenerator (
	name = "MEMBER_SEQ_GENERATOR",
    sequenceName = "MEMBER_SEQ",
    initalValue = 1, allocationSize = 50
)
public class Member {
    ...
}

Member 엔티티를 다음과 같이 설정하고 트랜잭션을 아래와 같이 실행했다.

...
em.persist(memberA); // 1, 51
em.persist(memberB); // MEMORY
em.persist(memberC); // MEMORY
...

 

맨 처음 memberA를 영속성 컨텍스트에 저장했을 때는 다음의 쿼리가 나간다.

Hibernate:
    call next value for MEMBER_SEQ
Hibernate:
    call next value for MEMBER_SEQ

엥? 왜 쿼리가 두번나가지?

맨 처음 쿼리는 MEMBER_SEQ의 값을 1로 만든다. (더미 쿼리)

두 번째 쿼리에서 MEMBER_SEQ의 값을 51로 만든다. (나 50까지 메모리에서 쓸거야)

따라서 memberA 에서만 2번의 쿼리가 나가고

memberB와 memberC는 쿼리가 나가지 않기 떄문에 성능 최적화를 할 수 있다.

MEMBER_SEQ가 51이 되면 시퀀스 값을 100으로 증가시켜 51~100을 메모리에서 사용한다.

(참고 : allocationSize의 기본값은 50이다.)

 

웹 서버가 여러대라도 이미 자기만의 영역 값을 따로 할당받기 때문에 동시성 이슈가 없다.

 

TABLE

키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략

 

모든 데이터베이스에 적용이 가능하다는 장점이 있지만,

테이블을 직접 사용하므로 데이터베이스 락이 걸릴 수 있는 등 성능 문제가 있다.

 

운영에서 테이블 전략을 쓰기는 사실상 부담스러움..

이런게 있다 정도만 이해하자.

 

AUTO

DB 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택 (기본 값)

 

AUTO 전략의 장점은 데이터베이스를 변경하더라도 코드를 수정할 필요가 없다.

키 생성 전략이 확정되지 않은 개발 초기단계에서 편하게 사용이 가능하다.

 

기본 키 타입은 뭐로 정해야 할까?

Integer는 값이 10억이 한계 범위다.

int는 32bit의 메모리가 필요하고, long은 64bit의 메모리가 필요하다. (공간 차이 2배)

하지만 Long으로 바꾼다고 해서 요즘에는 전체 애플리케이션의 범위에서 봤을 때 거의 영향을 주지 않는다.

Integer로 데이터를 10억을 채우고 Long으로 전환하는 것은 어려움에 많기 때문에

Long을 사용하자.

 

권장하는 식별자 전략은?

기본 키 제약 조건은 null 이 아니고 유일해야 하며, 변하면 안된다

주민 등록번호도 기본 키로 적절하지 않다.

권장하는 방식은 Long형 + 대체키 + 키 생성전략 사용을 이용하는 것이다.

 

대체키(대리키) 란?

비즈니스 로직과 전혀 관련이 없는 키

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

 

참고

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

반응형

'JPA' 카테고리의 다른 글

JPA 를 공부하면서 알게 된 내용 정리 1  (0) 2022.01.26
다양한 연관관계 매핑  (0) 2020.04.09
연관관계 매핑 기초  (0) 2020.04.06
영속성 관리 - 내부 동작 방식  (0) 2020.04.03
JPA를 왜 써야 하는가?  (0) 2020.04.03

댓글