기본 키 매핑⭐️

Q. id의 데이터 타입은 어떤 게 좋나요? A. Long을 써야한다. int : 0 때문에 X Integer : Integer의 저장범위 때문에 10억이 좀 넘어가면 한바퀴 돈다. Integer와 Long을 비교해봤을 때 공간은 2배차이 나지만 애플리케이션 전체를 봤을 때 거의 영향을 주지 않는 정도이다. Integer 범위가 10억을 넘어갈 때 타입을 바꾸는 것이 더 어렵다.

기본 키 매핑 어노테이션

  • @Id

  • @AutoGenerated

기본 키 매핑 방법

  • 직접 할당 : @Id만 사용

  • 자동 생성 : @GeneratedValue

    • IDENTITY : DB에 위임(MYSQL => AUTO-INCREMENT)

    • SEQUENCE : DB Sequence Object 사용(ORACLE) =>@SequenceGenerator 필요

    • TABLE : 키 생성용 테이블 사용(모든 DB 사용 가능) =>키 생성에 최적화❌ 성능👎🏻 =>TableGenerator 필요

    • AUTO : DB에 따라 자동 지정(기본값)

IDENTITY 전략 특징

  • JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행 =>DB에 들어가고 나서야 ID 확인 가능! =>영속성 컨텍스트(1차 캐시)에 들어있으려면 PK가 있어야하는데 DB에 넣기 전에 PK를 알 수 없다! 그렇기 때문에 JPA는 엔티티를 넣지 못한다! 그래서 IDENTITY 전략에서만 예외적으로 em.persist() 시점에 DB에 INSERT 쿼리를 날린다! 그래서 모아서 insert하는 버퍼링이 IDENTITY 전략에서는 불가능!! =>실무에서는 버퍼링해서 쓰기하는 것이 크게 메리트가 있지는 않다. 한 트랜잭션 내에서 쿼리가 여러개 또는 소수로 나가는 것의 차이가 성능에 큰 영향을 주지는 않기 때문이다. DB에 insert하고 바로 데이터를 조회하는 경우 select로 SQL 날리지 않는다. 왜냐하면 JDBC 내부에 DB에 insert하고 이를 바로 리턴하는 경우 로직이 구현되어있기 때문이다! 이러한 내부 구현 덕분에 JPA가 내부적으로 id값을 들고와서 영속성 컨텍스트에 PK값으로 사용할 수 있다! 따라서 em.persist()하고 바로 데이터 조회 시에 바로 값을 얻을 수 있다!

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

SEQUENCE 전략 특징 버퍼링 가능! PK값만 가져오고 영속성 컨텍스트나 SQL 저장소에 쌓기 가능하기 때문

SEQUENCE 전략 역시 DB에 커밋된 후에 알 수 있다! Sequence Object는 DB가 관리하는 것이기 때문이다.

SEQUENCE 전략일 때 JPA가 동작하는 방식

하지만, 영속성 컨텍스트에 엔티티 넣으려면 항상 무조건 PK가 있어야한다!! 그래서 SEQUENCE 전략은 DB에서 PK만 가져와서 영속성 컨텍스트에 등록하는 방식으로 동작한다!(DB에서 PK값만 얻고, Member 클래스의 id 필드에 값 넣어준다. 그 다음 영속성 컨텍스트에 저장)

이 때 DB에 insert쿼리는 날아가지 않는다. PK값만 얻고, 필요에 따라 버퍼링 해야하기 때문이다! insert쿼리는 트랜잭션 커밋(tx.commit())하는 시점에 들어간다! 주의 : allocationSize 기본값 = 50

속성

설명

기본값

name

식별자 생성기 이름

필수

sequenceName

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

hibernate_sequence

initialValue

DDL 생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정

1

allocationSize

시퀀스 한 번 호출에 증가하는 수

데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값 을 반드시 1로 설정해야함

50⭐️⭐️⭐️⭐️⭐️

catalog, scheema

DB catalog, schema 이름

성능에 대한 고민 em.persist()를 할 때마다 DB에서 값을 가져오면 매번 네트워크를 왔다갔다 해야되기 때문에 복잡하기도 하고, 성능상 좋지 않다. 차라리 insert쿼리 한번 날리는 것이 나을 수도 있다. =>allocationSize 이용해서 성능최적화 가능!

allocationSize 속성 이용 성능 최적화 방법

메모리에 allocationSize만큼 미리 올려놓고 쓰는 방식! DB에서 150 => 메모리에 가져와서 쓰고, 다 쓰면 다시 51~100 가져와서 쓰고.

여러 웹 서버(WAS)가 있어도 동시성 이슈 없이 해결된다! 동시호출하더라도 각각 숫자를 미리 확보해놓음. nextCall 호출하고 값을 받으면 51이면 1~50 사용 가능하구나. 다른 애가 101 받으면 51~100 사용가능하구나. 이런식으로 동작하기 때문에 동시성 문제 ❌

사이즈를 10000개 정도 넉넉하게 하면 좋지 않나? => 웹서버를 내리는 시점에서 데이터가 날아감으로써 중간에 구멍이 생긴다. 낭비가 생기는 것이다.

보통 조직에서 쓰는 타입 정해져있다.

TABLE 전략 - 속성

속성

설명

기본값

name

식별자 생성기 이름

필수

table

키생성 테이블명

hibernate_sequence

pkColumnName

시퀀스 컬럼명

sequence_name

valueColumnName

시퀀스 값 컬럼명

next_val

pkColumnValue

키로 사용할 값 이름

엔티티 이름

initialValue

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

0

allocationSize

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

50

catalog, schema

데이터베이스 catalog, schema 이름

uniqueConstraints(DDL)

유니크 제약 조건을 지정할 수 있다.

⭐️권장하는 식별자 전략 - 기본키 특성을 생각해보자!

  1. null이면 안 된다.

  2. 유일해야 한다.

  3. 변하면 안된다. 애플리케이션의 생명주기 동안에는 변하거나 없어지면 안된다. =>미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자! =>예를 들어 주민등록번호도 기본 키로 적절하지 않다.

  4. 권장: Long형 + 대체키 + 키 생성전략 사용 Long : 10억이 넘어도 문제 없이 사용하기 위해. ex) AutoIncreament or Sequence Object 둘 중 하나 또는 New Id, 랜덤값을 조합한 회사내 규칙

실례로, 주민번호를 PK로 사용하다가 정부의 방침으로 주민번호를 저장하면 안 되는 상황. => 단순히 PK 문제가 아니라 PK를 FK로 쓰던 테이블까지도 전체적으로 영향이 가기 때문이다!

자연키란? 비즈니스적으로 의미있는 키. ex) 주민등록번호 등

Last updated