본문 바로가기

개발/Spring

[Spring] JPA 기본 키 생성 전략

JPA 가 제공하는 DB 기본 키 할당 전략은 직접 할당 방식자동 생성 방식 두 가지가 있다.

 

직접 할당은 컬럼에 @Id 만 사용하여 생성하는 방식이 있고,


@Id가 적용 가능한 Java Type은 아래와 같다.

  • Java 기본형(int, double, long ...)
  • Java Wrapper 형
  • String
  • java.util.Date
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger

해당 전략은 em.persist()로 Entity를 저장 하기 전,

Application에서 직접 기본 키를 할당해주어야 한다.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("JPA");

EntityManager em = emf.createEntityManager();

User user = new User();
user.setName("gillog");
user.setId(1);

em.persist(user);

자동 생성은 @Id와 @GeneratedValue를 같이 사용하여 생성하는 방식이 있다.

@GeneratedValue에는 4가지 전략이 있다.

 

IDENTITY

@Id
@Column(unique = true)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
  • 기본 키 생성을 DB에 위임하는 전략이다.
  • auto_increment 기능을 제공 해 준다.
  • 기본 키 값을 자동으로 생성하는 DBMS에서 사용한다.
  • MYSQL, PostgreSQL, SQL Server, DB2에서 사용

JPA에서는 트랜잭션 commit 시점에 insert SQL을 실행한다.

그 직전까지 SQL저장소에 insert 문을 모아 두었다가 flush()가 호출될 때 DB에 접근해서 모든 SQL문을 전달한다.

이때 기본 키를 참조하는 로직이 있거나, 영속성 객체를 만들 때 문제가 생길 수 있다.


hibernate에서는 이를 위한 최적화 방법을 제공한다. hibernate는 JDBC3에 추가된 Statement.getGeneratedKeys() 메서드를 이용해서 데이터를 저장함과 동시에 생성된 기본 키 값을 바로 건져온다. 이를 통해 hibernate는 데이터베이스와 2번 통신(먼저 저장, 이후 id얻어 옴)해야 할 것을 1번만 통신할 수 있게 최적화했다.

여담으로, 공식 문서에 따르면 hibernate 5.3 버전에서는 AUTO전략이 아닌 경우에 엔티티 생성 시 Insert 쿼리를 쓰기 지연하려고 했는데, IDENTITY나 SEQUENCE전략을 따르는 엔티티들이 연관관계(@OneToMany 등)로 묶여 있는 다른 엔티티들과 같은 트랜잭션에 있을 때 문제를 발생시킬 수 있다고 한다.

그래서 hibernate는 5.4버전부터 내부적 알고리즘을 사용하여 Insert쿼리에 쓰기 지연을 해야 하는지 아니면 바로 데이터베이스에 쿼리를 보내야 하는지를 판단하게 했다. 혹시나 5.3버전의 방침인, Insert쿼리에 쓰기 지연이 적용되는 것 때문에 문제가 발생하면 설정에 hibernate.id.disable_delayed_identity_inserts=true 를 명시해 놓으면 Insert시 쓰기 지연이 동작하지 않게(Insert쿼리들이 바로 데이터베이스에 전달되어 엔티티들이 생성 즉시 저장되도록) 할 수 있다.

 


SEQUENCE

@Entity
@SequenceGenerator(name = "USER_SEQ_GENERATE"
        , sequenceName = "USER_SEQ"
        , initialValue = 1
        , allocationSize = 1)
public class User {

    @Id
    @Column(unique = true)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_SEQ_GENERATE")
    private Long id;

    @Column
    private String name;
}
  • 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트
속성 설명  기본값
name 식별자 생성기 이름 필수
sequenceName 데이터베이스에 등록되어 있는 시퀀스 이름 hibernate_sequence
initialValue DDL 생성 시에만 사용, 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다. 1
allocationSize 시퀀스 호출시 증가하는 수(성능 최적화에 사용), 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다. 50
catalog, schema 데이터베이스 catalog, schema 이름  

hibernate는 데이터베이스 시퀀스 오브젝트에 접근하는 횟수를 줄이기 위해 allocationSize 라는 옵션을 만들어 두었다. 여기에 설정한 값 만큼 데이터베이스에서 한 번에 시퀀스를 받아오고 그 값을 메모리에 저장해 둔다. 이후 새로운 엔티티가 영속 컨텍스트에 추가될 때마다 메모리에서 식별자 값을 가져와서 할당하는 것이다.

 

allocationSize 를 1로 주었다면 엔티티 생성시마다 데이터베이스에 접근할 것이고, 50으로 주었다면 1번 데이터베이스에 접근해서 1~50을 받아온 뒤 메모리에 저장해 두고, 50번째의 엔티티가 생성될 때 까지 데이터베이스 대신 메모리에서 식별자를 할당해 주는 방식인 것이다. 내 프로그램에서 Insert 쿼리의 성능이 매우 중요한 상황이라면 값을 비교적 크게, 그렇지 않다면 1로 주면 된다.


AUTO

@GeneratedValue(strategy = GenerationType.AUTO)

  • AUTO는 hibernate가 DB 방언에 따라 자동으로 IDENTITY, SEQUENCE, TABLE 전략을 자동으로 선택한다.
  • 기본 전략이 AUTO로 설정되어 있는데, 만약 SEQUENCE나 TABLE전략이 선택되면 해당 시퀀스나 키 생성용 테이블을 미리 만들어 놓아야 한다.​

 

TABLE

  • 키 생성 전용 테이블을 생성해서 키 값을 관리한다.
  • 최적화 되지 않은 테이블에서 키를 생성하기 때문에 성능상의 이슈가 발생할 수 있다.
  • 잘 사용하지 않는 듯 하다.

 

 

참고 - https://velog.io/@gillog/JPA-%EA%B8%B0%EB%B3%B8-%ED%82%A4-%EC%83%9D%EC%84%B1-%EC%A0%84%EB%9E%B5IDENTITY-SEQUENCE-TABLE

 

[JPA] 기본 키 생성 전략(IDENTITY, SEQUENCE, TABLE)

자바 ORM 표준 JPA 프로그래밍JPA가 제공하는 DB 기본 키 할당 전략은 직접 할당 방식, 자동 생성 방식 두 가지이다.이 중 직접 할당 방식은 Application에서 기본 키를 직접 할당하는 방식이다.자동 생

velog.io

참고 - https://velog.io/@strawberry/JPA-%EA%B8%B0%EB%B3%B8-%ED%82%A4-%EB%A7%A4%ED%95%91

 

JPA 기본 키 매핑 전략(identity, sequence, auto)

그동안 springboot2 로 수많은 엔티티들을 만들면서 수많은 기본 키를 생성했었다. 하지만 기본 키 생성에 관한 심도있는 고민이 없었다는 생각이 들어서 공부했던 내용을 간단히 정리해 볼까 한다

velog.io