여러 entity를 table에 생성을 해야 하는 경우, 하나씩 n번 생성하는 것보다는 bulk로 묶어서 생성하는 편이 여러모로 좋다.
JPA의 구현체에 .saveAll
로 Iteratable
을 인자로 받는 메소드가 있어서 아주 적은 노력으로 구현했지만, 실제 디버깅 레벨로 쿼리를 출력해보니 하나씩 처리하고 있었다. 인터페이스는 마치 INSERT INTO {TABLE} VALUES ([..., ...])
꼴의 쿼리로 변환될 것처럼 보였는데, 하나씩 처리하고 있으니 그 원인을 찾아봤다.
역시 가장 먼저 이와 관련된 설정 값이 있다.
spring.jpa.properties.hibernate.jdbc.batch_size=20
hibernate에게 batch 처리를 가능하도록 그 크기에 대한 설정 값을 0보다 크게 바꿔야 한다. 이렇게 해도 동작은 하지만 여러 entity를 다루는 구간에서는 또 생각처럼 동작하지 않는다. 그 원인은 batch를 관리하는 방법에서 시작된다.
하나의 트랜잭션에 영속성을 가지는 여러 개의 타입이 나타날 경우 hibernate는 새로운 batch를 만들어 기록한다. 예를 들어 A 타입을 생성하고, B 타입을 생성한 뒤 다시 A 타입을 생성할 경우에 이 A 타입은 처음 생성한 A 타입과 같음에도 불구하고 다른 batch로 묶인다. 이렇게 되면 나중에 insert 할 때 하나의 쿼리로 묶이지 않는다.
이를 해결하기 위해서 서로 다른 batch에 있어도 같은 타입이면 하나의 batch로 실행해주는 옵션을 켠다.
spring.jpa.properties.hibernate.order_inserts=true
이렇게 하면 의도한 대로 하나의 쿼리로 bulk insert 되는 entity도 있지만 안 되는 entity도 있다.
바로 아래와 같이 id를 auto increment로 생성하는 녀석들이 bulk insert가 안된다.
@GeneratedValue(strategy = GenerationType.IDENTITY)
IDENTITY
로 id를 생성한다는 것은 직접 db에 insert를 실행해봐야 알 수 있는 값이다. 이는 hibernate가 영속성을 관리하는 측면에서 보면 그 철학과 어긋난다. 영속성 또는 트랜잭션이 끝나는 시점에 변화가 있는 entity를 db에 반영을 해줘야 하는 입장인데, db에 의존적인 명령이니 앞뒤가 안 맞는 것이다.
그래서 이런 이유로 JPA는 IDENTITY
에 대해서 batch를 지원하지 않는 방향으로 갔다.
'java' 카테고리의 다른 글
Type Erasure와 ParameterizedTypeReference (0) | 2021.01.02 |
---|---|
gradle로 spring-boot 프로젝트 설정 (0) | 2019.11.19 |
댓글