JPA Entity 객체의 Id 필드를 지정할 때, Generation type을 identity, sequence, table 등의 전략으로 설정할 수 있다. (관련 포스팅)
이 때 type을 sequence로 설정하고 persistence.xml 내 DDL auto 프로퍼티를 create로 주게 되면(꼭 해당 상황에서만 발생하지는 않지만)
sequence 생성 및 최초 실행 시 next 값을 두 번 호출한다.
<property name="hibernate.hbm2ddl.auto" value="create" />
// 저장할 Member Entity
@Entity
@SequenceGenerator(
name = "MEMBER_SEQ_GENERATOR") // default 값
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "MEMBER_SEQ_GENERATOR")
private Long id;
}
// main
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member = new Member(); // Entity 객체 생성
member.setUsername("HelloA");
em.persist(member); // 영속 - sequence 조회 후 자동 부여
tx.commit();
} catch (Exception e) {
tx.rollback();
}
sequence 값이 호출되는 시점은 em.persist(member) 실행 시점이다.
next value가 두 번 호출되는 것은 시퀀스 생성 시 increment 값과 관련이 있다.
hibernate는 성능 등의 이유로 increment default 값을 50으로 두고 있다.
시퀀스 값을 50씩 증가하게 두고, 그 사이의 값을 영속성 컨텍스트에서 사용하기 위함이다.
이 때 H2 DB에서는 최초 call next value 호출에 1을 return한다.
최초에 1을 return 받으면 캐싱해놓고 사용할 범위가 없다.
increment size는 일부 범위 시퀀스를 내부적으로 가지고 있으려는 용도인데, 1 즉, initial value를 return 받으면 캐싱할 범위가 없기 때문에 2번 호출하는 것이다.
두 번 호출하면 51 value가 리턴되고, 그렇게 되면 1 ~ 50 까지의 값을 하나의 App 내 영속성 컨텍스트에서 사용 가능하다.
최초 리턴( initial value)이 아니라서 next value(max)만 리턴받으면 초기값 세팅은 어떻게?
Application의 hibernate에서는 시퀀스 사이의 값들을 내부적으로 캐싱해놓고 사용하기를 원한다.
1을 리턴 받았을 때는 한 번 더 호출하여 값을 세팅하지만, 최초값이 아닌 경우는 한 번만 호출하여 max값을 설정한다.
시작값은 return 받은 값(캐싱해놓고 사용하는 max값)에서 increment value를 뺀 후 계산하여 시작 값을 지정한다.
// 만약 return 받은 next value가 101이라면 대략 이런 계산으로 초기값 세팅
101 ( max 값 ) - 50 (increment value) = 51
따라서 next value 한 번의 호출로 세팅이 가능하다. 이렇게 초기값을 51로 세팅해 놓은 후, max값 (100) 까지 자유롭게 사용할 수 있기 때문이다.
hibernate-core 코드에서 확인하기
해당 내용은 hibernate-core에서 확인할 수 있다.
아래는 em.persist() 메소드 호출한 후 동작하는 내부 로직 중 PooledOptimizer class 내의 sequence 세팅 로직 일부 소스코드이다.
allocation size가 1이 아닐 때는 Optimizer 구현체 중 PooledOptimizer를 호출한다.
allocationSize(Increment)가 1일 때는?
시퀀스 증가값을 1로 세팅하는 것은 캐싱하여 관리할 시퀀스 범위 지정 없이, 하나의 객체 생성할 때마다 sequence를 새로 호출하겠다는 의미이다.
따라서 매 객체 생성시마다 next value를 호출하기 때문에 최초 호출시에도 1번 호출하여 next value를 받고 해당 시퀀스 값을 사용하면 된다. 그 후 또 새로운 객체를 만들 때 next value로 두 번째 값을 call하여 사용하면 된다. (캐싱할 범위 지정 필요하지 않기 때문)
allicationSize=1 일 때는 Optimizer 구현체 중 NoopOptimizer를 호출한다.
OptimizerFactory에서 사이즈별로 구현체 반환하는 로직
optimizerfactory에서 increment size별로 optimizer 구현체를 구분하여 반환한다.
'Programming > Database & Query' 카테고리의 다른 글
[JPA] 자바 ORM 표준 JPA 프로그래밍 : 다양한 연관관계 매핑 (0) | 2022.02.10 |
---|---|
[JPA] 자바 ORM 표준 JPA 프로그래밍 : 연관관계 매핑 기초 (0) | 2022.01.31 |
[JPA] 자바 ORM 표준 JPA 프로그래밍 : 엔티티 매핑 (0) | 2022.01.20 |
[JPA] 자바 ORM 표준 JPA 프로그래밍 : 영속성 관리 - 내부 동작 방식 (0) | 2022.01.16 |
[JPA] 자바 ORM 표준 JPA 프로그래밍 : JPA란, JPA 시작하기 (0) | 2022.01.16 |