[3] JPA 프로그래밍 (5) - Entity Mapping 2 / 기본 키 매핑 / @Id @GeneratedValue

김정욱·2021년 3월 3일
0

[3] JPA 프로그래밍

목록 보기
5/15
post-thumbnail

기본 키 매핑

기본 키 매핑관련한 어노테이션은 크게 @Id / @GeneratedValue 가 있음
그 중, @GeneratedValue와 관련한 전략(strategy)에 집중해서 알아보자

1) @Id : 현재 필드가 기본키임을 알리기 위한 어노테이션
2) @GeneratedValue : 기본키 값 생성에 관련된 어노테이션

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

@GeneratedValue

  • 기본 키 값을 자동생성하는 어노테이션으로 여러가지 전략이 있다.
    • IDENTITY : DB에 위임
    • SEQUENCE : DB의 시퀀스 오브젝트를 사용
    • TABLE : 키 생성용 테이블을 사용
    • AUTO : 방언에 따라 자동 지정 (default 전략이다)

IDENTITY 전략

개념

  • 주로 MySQL 사용시 선택하는 전략
  • DB에게 알아서 증가하도록 위임하는 방법
    (MySQL의 auto increment 수행함)
public class Member {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id; 
}

: MySQL로 실행시, auto_increment 속성이 적용된다


주의할 점 (매우 중요)

  • JPA는 영속성 컨텍스트를 통해 transaction.commit 시점에 SQL이 실행
    --> DB가 알아서 기본키 값을 증가시키려면 현재 어디까지 번호가 발급되었는지 알아야하는데 JPA가 이렇게 동작하면 알 수가 없다!!
  • 그래서 JPA에서 IDENTITY 전략을 사용하면 영속화와 동시에 SQL이 실행되도록 설정되어있다!! (예외처리됨)
  • em.persist(member) 하면 바로 SQL 수행되고 DB 식별자 번호를 반환해서 다음 번호로 영속성 컨텍스트에 저장된다!

SEQUENCE 전략

개념

  • DB의 시퀀스 오브젝트를 사용한다!
  • DB 시퀀스란 ?
    : 유일한 값을 순서대로 생성하는 특별한 DB 오브젝트
  • Oracle 등에서 사용

사용 순서

@Entity
/* SequenceGenerator로 Sequence를 생성 */
@SequenceGenerator(
  name = "MEMBER_SEQ_GENERATOR", 
  sequenceName = "MEMBER_SEQ", // 매핑할 데이터베이스 시퀀스 이름 
  initialValue = 1,
  allocationSize = 1)
public class Member {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE,
                  generator = "MEMBER_SEQ_GENERATOR")
                  // 만든 SequenceGenerator 연결
  private Long id; 
}

1. @SequenceGenerator 선언
: 제너레이터 이름 / 시퀀스 이름 / 속성 값 을 설정
2. @GeneratedValue(...) 지정
: 전략(strategy)를 SEQUENCE / generator를 위에서 만든 시퀀스 제너레이터 연결


동작 원리

  1. em.persist(Member)로 영속화되어 등록될 때 sequence number가 몇까지 나왔는지 모르기 때문sequence call을 수행
  2. sequence call로 알아온 number를 통해 pk로 등록 후 영속성 컨텍스트에 등록

주의할 점

  • 매번 em.persist()할 때마다 sequence call을 하면 비효율적이지 않을까? 하는 의문이 든다
  • 그래서 제공되는 옵션이 allocationSize이다
  • 어떻게 ?
    • 기본값인 50이 가지는 의미50번호만큼 미리 메모리에 할당받아 두겠다는 것
    • 미리 50까지는 메모리에 저장해서 증가시키기 때문에 DB의 sequence number는 51이 등록되어 있을 것임
    • 50번의 번호가 가득 찼을 때 다음 sequence number을 알아내는 sequence call이 수행되어 나름 효율적인 구조를 가지게 된다

TABLE 전략

개념

  • 키를 생성하고 관리하는 테이블을 하나 만들어서 DB 시퀀스를 흉내
  • 모든 DB에 대해 적용 가능
  • 성능이 좋지 못해 실무에서 잘 사용되지는 않는다
  • 시퀀스처럼 동작하기 때문에 역시 영속성 컨텍스트문제를 겪게 되고,
    시퀀스와 동일하게 allocationSize옵션으로 동작된다
  • 사용 예시
@Entity
@SequenceGenerator(
  name = "MEMBER_SEQ_GENERATOR", 
  table = "MY_SEQUENCES", // 데이터베이스 이름 
  pkColumnValue = "MEMBER_SEQ",
  allocationSize = 1)
public class Member {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE,
                  generator = "MEMBER_SEQ_GENERATOR")
  private Long id; 
}

AUTO 전략

  • Default로 설정되는 값
  • 방언에 따라 위의 3가지 전략을 자동으로 지정
    (MySQL인지 / Oracle인지 등등)

권장하는 식별자 전략

설명

  • 기본키 제약조건을 만족
    • not null
    • 유일성과 최소성
    • 변하면 안된다
  • 미래까지 조건들을 만족하는 자연키는 찾기가 어렵다
    --> 대체키를 사용하자
    • 자연키(natural key) ?
      : 비즈니스적으로 의미가 있는 키
        (전화번호, 주민번호)
    • 대체키(Generate Value) ?
      : 비즈니스적으로 상관없는 키 (랜덤값 등등)

ex) 주민번호가 기본키일 경우
: 갑자기 개인정보 보호목적으로 DB에 저장하지 못하게 된다면 관련된 모든 테이블을 수정해야 함
즉, 모든 테이블을 Migration 해야하는 끔찍한 상황!


정리

  • 기본키 제약조건을 만족
  • 권장하는 식별자 구성 전략
    : (Long형) + (대체키) + (적절한 키생성 전략) 이용
    • Long형을 사용해야 하는 이유
      : int는 0을 사용해서 null과 0의 구분이 애매해서 오류날 수 있음
      Integer는 10억이 넘어가면 순환해서 오류가 날 수 있음
      Long이 위 두 문제를 어느정도 해결하는 좋은 합의 지점임!
    • 대체키 사용
      : 랜덤값 / UUID 등 비즈니스와 관계없는 값
    • 적절한 키 생성 전략
      : IDENTITY / SEQUENCE 추천
profile
Developer & PhotoGrapher

0개의 댓글