13. 기본 키 매핑

김성수·2023년 4월 6일
0

⚡ 생각대로 살지 않으면 사는대로 생각한다.

⚡ 나는 어차피 잘 될 놈이다. 이미 잘 되고 있고, 계속해서 잘 되고 있다.


기본 키 매핑 어노테이션

  • @Id
  • @GeneratedValue

기본 키 매핑 방법

  • 직접 할당: @Id만 사용
  • 자동 생성(@GeneratedValue(strategy = 전략))
    • IDENTITY: 데이터베이스에 위임, MYSQL
    • SEQUENCE: 데이터베이스 시퀀스 오브젝트 사용, ORACLE
      • @SequenceGenerator 필요
    • TABLE: 키 생성용 테이블 사용, 모든 DB에서 사용
      • @TableGenerator 필요
    • AUTO: 방언에 따라 자동 지정, 기본값

IDENTITY 전략 - 특징

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

    날리는 쿼리를 보면, insert문의 valuesid값으로 null이 들어가는데, IDENTITY 전략으로 인해서 실제 들어간 DB를 확인해보면, 기본값이 알아서 들어가있다.

  • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
    (예: MySQL의 AUTO_INCREMENT)

  • 해당 전략을 사용하면PK에 값을 넣어주면 안 된다.

  • JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행하는데 IDENTITY 전략만큼은 em.persist() 시점즉시 INSERT SQL 실행하고 DB에서 식별자를 조회한다.

    이처럼 print문이 SQL 쿼리를 날린 이후에 나오는 걸 확인 할 수 있다. 즉, em.persist()시점에 SQL을 날리는 것을 확인할 수 있다.

  • @Id값을 알 수 있는 시점 : DB에 값이 들어가봐야 알 수 있다.

    • 원래 영속성 컨텍스트에 들어가려면 무조건 PK 값이 있어야 한다.
      • 1차캐시에서 key@Id로 관리한다.
  • AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있음.

IDENTITY 전략 - 매핑

@Entity 
public class Member { 
 @Id 
 @GeneratedValue(strategy = GenerationType.IDENTITY) 
 private Long id; 

SEQUENCE 전략 - 특징

  • 데이터베이스 시퀀스유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트(예: 오라클 시퀀스)
  • 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용

되도록 Long타입을 사용하자. 아니 그냥 Long을 사용하면 된다❗

SEQUENCE 전략 - 매핑

@Entity 
@SequenceGenerator( 
 name = “MEMBER_SEQ_GENERATOR", 
 sequenceName = “MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
 initialValue = 1, allocationSize = 1) 
public class Member { 
 @Id 
 @GeneratedValue(strategy = GenerationType.SEQUENCE, 
 generator = "MEMBER_SEQ_GENERATOR") 
 private Long id;

영속성 컨텍스트에 객체를 넣을 (em.persist())PK가 필요한데, Sequence에서 PK를 가져와야한다.
@Id의 전략에서 Sequence임을 확인하고,

위의 로그와 같이 call next value for MEMBER_SEQ를 통해서 Sequence 값을 얻어오고 영속성 컨텍스트에 넣는 시점(em.persist())@IdSequence값을 넣고, 영속성 컨텍스트에 저장한다.
그리고 tx.commit()시점에 쿼리를 날린다.

🤔그럼 일일이 Sequence를 불러와야 될 것 같은데, 성능상의 문제가 생기지 안을까?

그래서 Sequence전략에는 allocationSize를 지정해서, Sequence 사이즈를 한번에 불러올 크기를 지정할 수 있다.
예를 멤버 인스턴스 3개를 생성하는 코드를 작성하고, allocationSize를 1로 지정했을 때 와 기본값(50)이상으로 지정했을 때를 비교해보면,

다음과 같은 코드에서 allocationSize를 1로 줬을 때,

시퀀스를 매번 호출함을 알 수 있다.

기본값을 주면, Sequece가 두번 호출된다.
50개로 지정했는데, 처음 호출했을 때 값이 1이라서 한번 더 호출하게 되는데,
첫 번째 em.persist()시 PK는 시퀀스를 호출해서 더미값을 주고, 두번째, 세번째는 메모리에서 호출하게 된다.

DB에 먼저 시퀀스를 올려놓고, 메모리에서 그 개수만큼 쓰는방식인데, 10,000개룰 줘도 되지만, 이렇게 되면 웹서버를 내리는 시점에 날라간다. 그럼 중간에 숫자 구멍이 생긴다. 그래서 50~100 정도가 적당하다.

이제... 이게 참 기가 막힌게 이제... 여러 웹서버가 있어도 동시성 이슈없이 다양한 문제들이 해결됩니다.

@IdSEQUENCE를 지정하게되면, 아래와 같이 hibernate가 만드는 기본 시퀀스인 hibernate_sequence에서 값을 가져와서 DB에 저장한다.

❗만약 테이블마다 SEQUNECE를 따로 관리하고 싶으면, 클래스레벨에서 @SequenceGenerator를 가지고 매핑을 하면 된다.
※ 참고: 위의 코드...

SEQUENCE - @SequenceGenerator

❗주의 : allocationSize 기본값 = 50

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


@SequenceGenerator에 설정한대로 sequence를 생성했다.

TABLE 전략

  • 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
  • 장점: 모든 데이터베이스에 적용가능
  • 단점: 성능
    • 다른 전략들은 숫자를 뽑아내는데 최적화가 되어있지만, Table전략은 최적화가 되어있지 않아서 성능에 이슈가 있다.

TABLE 전략 - 매핑

create table MY_SEQUENCES ( 
 sequence_name varchar(255) not null, 
 next_val bigint, 
 primary key ( sequence_name ) 
 }

위의 SQL문이 아래 JPA 적용 코드와 동일

@Entity 
@TableGenerator( 
 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; 

위의 코드 결과는 아래와 같다.

TABLE 전략은 SEQUENCE를 별도의 테이블로 관리하는 전략이다.
운영에는 TABLE전략을 잘 사용하지 않는다.

@TableGenerator - 속성

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

권장하는 식별자 전략

  • 기본 키 제약 조건: null 아님, 유일, 변하면 안 된다.
    • null이 아닌 것과 유일한건 나름 지킬 수 있으나, 변하면 안 된다하는 조건을 끝까지 지키는 것이 어렵다.
  • 미래까지 이 조건을 만족하는 자연키(비즈니스적으로 의미있는 키 ex] 주민등록번호, 전화번호)는 찾기 어렵다. 대리키(대체키)(비즈니스와 전혀 상관없는 키)를 사용하자.
  • 예를 들어 주민등록번호도 기본 키로 적절하지 않다.
  • 권장: Long형 + 대체키 + 키 생성전략 사용
    • ❗비즈니스를 키로 끌고오는 것을 권장하지 않음.

정리까지 한다고 길었지만, 많은 것을 얻어갔다.. 후후
재밌네.... JPA...

얼마나 무지하게 사용했었는지 반성하게 된다...


-끝-

profile
쌩수 Git >> https://github.com/SsangSoo?tab=repositories

0개의 댓글