Spring JPA의 save() vs saveAll() vs bulk insert() (feat. db client)
이번에 면허 재검증 프로세스를 구성하면서 면허 검증 로깅에 관한 작업을 구성하게 되었다.
면허 검증에는 배치로 검증하는 과정이 있기 떄문에, 로깅도 한번에 저장되어 DB 팀에게 어느정도의 데이터를 한번에 저장할 수 있는지 문의드렸다.
단건 수행이 아닌 bulk insert로 하는 경우 더 많은 양의 데이터를 저장할 수 있다는 답변을 받고 해당 내용을 구현하였다.
작업 환경에서는 id의 채번 규칙을 auth_increment 전략을 사용하고 있었다.
JPA auth_increment ID 채번 방식
id의 채번 규칙을 auth_increment 전략을 사용하면 bulk insert가 가능할까?
해당 전략을 사용하게 되면, JPA규칙에 의해서 bulk insert는 사용할 수 없게 된다.
auto_increment 전략은 DB에 row를 insert할 때 그때마다 DB가 기본 키 값을 생성한다.
- INSERT INTO table ... 실행
- DB가 자동으로 ID 생성 (ex. AUTO_INCREMENT)
- 그 값을 JPA가 다시 받아와서 Entity 객체에 세팅
즉, insert를 하나하나씩 실행하고 DB에서 생성된 ID 값을 받아와야 하기 때문에 여러 개를 한꺼번에 insert할 수가 없다. 한 줄 한 줄 insert를 실행해야 한다.
Why does Hibernate disable INSERT batching when using an IDENTITY identifier generator
The Hibernate documentation says: Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator. But all my entities have this configuration: ...
stackoverflow.com
- 하이버네이트의 경우 트랜잭션 flush 시점에 DB에 insert 하는 write-behind 전략을 취함
- ID 값을 알기 전엔 Insert 불가
- insert 하면서 바로 ID를 받아와야 함
테스트 환경
springboot, spring-boot-starter-jpa, mysql-connector-j를 사용하였다.
plugins {
kotlin("jvm") version "1.9.25"
kotlin("plugin.spring") version "1.9.25"
id("org.springframework.boot") version "3.4.4"
id("io.spring.dependency-management") version "1.1.7"
kotlin("kapt") version "1.9.21"
kotlin("plugin.jpa") version "1.9.21"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation("org.springframework.boot:spring-boot-starter-web")
runtimeOnly("com.mysql:mysql-connector-j")
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
kapt("com.querydsl:querydsl-apt:5.0.0:jakarta")
}
다음과 같이 jdbc url에 profileSQL을 설정하면 수행되는 쿼리를 확인할 수 있다.
url: jdbc:mysql://localhost:33306/default?profileSQL=true
1. JPA의 save() 메서드 활용
가장 먼저 for문을 이용해서 save()메서드를 연속호출하여 인서트를 수행해보자.
@SpringBootTest
class UserRepositoryTest {
@Autowired
private lateinit var userRepository: UserRepository
@Test
fun save() {
val users = mutableListOf<UserEntity>()
for (i in 1..1000) {
users.add(UserEntity(email = "testUser$i@gmail.com", name = "testUser$i", phone = "010-1234-567$i", status = UserStatus.ACTIVE))
}
val start = System.currentTimeMillis()
for (user in users) {
userRepository.save(user)
}
val end = System.currentTimeMillis()
println("Time taken to save 1000 users: ${end - start} ms")
}
}
실행 시간 경과 결과는 다음과 같다.
Time taken to save 1000 users: 11681 ms
실행된 쿼리는 다음과 같다.
Mon Apr 07 22:52:08 KST 2025 INFO: [QUERY] SET autocommit=0 [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 1, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 22:52:08 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 0, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 22:52:08 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 22:51:56.39028','testUser999@gmail.com','testUser999','010-1234-567999','ACTIVE','2025-04-07 22:51:56.390281') [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 1, connection-id: 32, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
Mon Apr 07 22:52:08 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 0, connection-id: 32, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
Mon Apr 07 22:52:08 KST 2025 INFO: [QUERY] COMMIT [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 3, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Mon Apr 07 22:52:08 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 0, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Mon Apr 07 22:52:08 KST 2025 INFO: [QUERY] SET autocommit=1 [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 1, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 22:52:08 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 0, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 22:52:08 KST 2025 INFO: [QUERY] SET autocommit=0 [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 1, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 22:52:08 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 0, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 22:52:08 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 22:51:56.390284','testUser1000@gmail.com','testUser1000','010-1234-5671000','ACTIVE','2025-04-07 22:51:56.390285') [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 1, connection-id: 32, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
Mon Apr 07 22:52:08 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 0, connection-id: 32, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
Mon Apr 07 22:52:08 KST 2025 INFO: [QUERY] COMMIT [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 3, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Mon Apr 07 22:52:08 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 0, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Mon Apr 07 22:52:08 KST 2025 INFO: [QUERY] SET autocommit=1 [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 1, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 22:52:08 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 22:52:08 KST 2025, duration: 0, connection-id: 32, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
...
autoCommit을 0으로 만들고 commit 후 autocommit을 1로 바꾸는 작업을 반복하는 것을 확인할 수 있다.
이는 다음과 같이 SimpleJpaRepository 구현체에서 save() 메서드 호출시 @Transactional 어노테이션이 붙어 반복 연결을 맺고 커밋하기 때문이다.
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
@Override
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, ENTITY_MUST_NOT_BE_NULL);
if (entityInformation.isNew(entity)) {
entityManager.persist(entity);
return entity;
} else {
return entityManager.merge(entity);
}
}
}
// Hibernate 구현체
public class SessionImpl
extends AbstractSharedSessionContract
implements Serializable, SharedSessionContractImplementor, JdbcSessionOwner, SessionImplementor, EventSource,
TransactionCoordinatorBuilder.Options, WrapperOptions, LoadAccessContext {
@Override @SuppressWarnings("unchecked")
public <T> T merge(String entityName, T object) throws HibernateException {
checkOpen();
return (T) fireMerge( new MergeEvent( entityName, object, this ) );
}
private Object fireMerge(MergeEvent event) {
try {
checkTransactionSynchStatus();
checkNoUnresolvedActionsBeforeOperation();
fastSessionServices.eventListenerGroup_MERGE
// merge 핵심 로직
.fireEventOnEachListener( event, MergeEventListener::onMerge );
checkNoUnresolvedActionsAfterOperation();
}
catch ( ObjectDeletedException sse ) {
throw getExceptionConverter().convert( new IllegalArgumentException( sse ) );
}
catch ( MappingException e ) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch ( RuntimeException e ) {
//including HibernateException
throw getExceptionConverter().convert( e );
}
return event.getResult();
}
}
// merge 이벤트 리스너 구현체
public class DefaultMergeEventListener
extends AbstractSaveEventListener<MergeContext>
implements MergeEventListener {
@Override
public void onMerge(MergeEvent event) throws HibernateException {
final EventSource session = event.getSession();
final EntityCopyObserver entityCopyObserver = createEntityCopyObserver( session );
final MergeContext mergeContext = new MergeContext( session, entityCopyObserver );
try {
onMerge( event, mergeContext );
entityCopyObserver.topLevelMergeComplete( session );
}
finally {
entityCopyObserver.clear();
mergeContext.clear();
}
}
@Override
public void onMerge(MergeEvent event, MergeContext copiedAlready) throws HibernateException {
final Object original = event.getOriginal();
// NOTE : `original` is the value being merged
if ( original != null ) {
final EventSource source = event.getSession();
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( original );
if ( lazyInitializer != null ) {
if ( lazyInitializer.isUninitialized() ) {
LOG.trace( "Ignoring uninitialized proxy" );
event.setResult( source.load( lazyInitializer.getEntityName(), lazyInitializer.getInternalIdentifier() ) );
}
else {
doMerge( event, copiedAlready, lazyInitializer.getImplementation() );
}
}
else if ( isPersistentAttributeInterceptable( original ) ) {
final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable( original ).$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
final EnhancementAsProxyLazinessInterceptor proxyInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
LOG.trace( "Ignoring uninitialized enhanced-proxy" );
event.setResult( source.load( proxyInterceptor.getEntityName(), proxyInterceptor.getIdentifier() ) );
}
else {
doMerge( event, copiedAlready, original );
}
}
else {
doMerge( event, copiedAlready, original );
}
}
}
private void doMerge(MergeEvent event, MergeContext copiedAlready, Object entity) {
if ( copiedAlready.containsKey( entity ) && copiedAlready.isOperatedOn( entity ) ) {
LOG.trace( "Already in merge process" );
event.setResult( entity );
}
else {
if ( copiedAlready.containsKey( entity ) ) {
LOG.trace( "Already in copyCache; setting in merge process" );
copiedAlready.setOperatedOn( entity, true );
}
event.setEntity( entity );
merge( event, copiedAlready, entity );
}
}
private void merge(MergeEvent event, MergeContext copiedAlready, Object entity) {
final EventSource source = event.getSession();
// Check the persistence context for an entry relating to this
// entity to be merged...
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
// !!! 영속성 컨텍스트에서 기존 엔티티 조회
EntityEntry entry = persistenceContext.getEntry( entity );
final EntityState entityState;
final Object copiedId;
final Object originalId;
if ( entry == null ) {
final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
originalId = persister.getIdentifier( entity, copiedAlready );
if ( originalId != null ) {
final EntityKey entityKey;
if ( persister.getIdentifierType() instanceof ComponentType ) {
/*
this is needed in case of composite id containing an association with a generated identifier, in such a case
generating the EntityKey will cause a NPE when trying to get the hashcode of the null id
*/
copiedId = copyCompositeTypeId(
originalId,
(ComponentType) persister.getIdentifierType(),
source,
copiedAlready
);
entityKey = source.generateEntityKey( copiedId, persister );
}
else {
copiedId = null;
entityKey = source.generateEntityKey( originalId, persister );
}
final Object managedEntity = persistenceContext.getEntity( entityKey );
entry = persistenceContext.getEntry( managedEntity );
if ( entry != null ) {
// we have a special case of a detached entity from the
// perspective of the merge operation. Specifically, we have
// an incoming entity instance which has a corresponding
// entry in the current persistence context, but registered
// under a different entity instance
entityState = EntityState.DETACHED;
}
else {
entityState = getEntityState( entity, event.getEntityName(), entry, source, false );
}
}
else {
copiedId = null;
entityState = getEntityState( entity, event.getEntityName(), entry, source, false );
}
}
else {
copiedId = null;
originalId = null;
entityState = getEntityState( entity, event.getEntityName(), entry, source, false );
}
// 엔티티 상태별 분기 처리
switch ( entityState ) {
case DETACHED:
entityIsDetached( event, copiedId, originalId, copiedAlready );
break;
case TRANSIENT:
entityIsTransient( event, copiedId != null ? copiedId : originalId, copiedAlready );
break;
case PERSISTENT:
entityIsPersistent( event, copiedAlready );
break;
default: //DELETED
if ( persistenceContext.getEntry( entity ) == null ) {
assert persistenceContext.containsDeletedUnloadedEntityKey(
source.generateEntityKey(
source.getEntityPersister( event.getEntityName(), entity )
.getIdentifier( entity, event.getSession() ),
source.getEntityPersister( event.getEntityName(), entity )
)
);
source.getActionQueue().unScheduleUnloadedDeletion( entity );
entityIsDetached(event, copiedId, originalId, copiedAlready);
break;
}
throw new ObjectDeletedException(
"deleted instance passed to merge",
null,
EventUtil.getLoggableName( event.getEntityName(), entity)
);
}
}
}
entityInformation.isNew(entity)가 true 라면 em.persist(entity)를 하지만, 아니라면 merge를 수행한다.
em.persist()의 경우 영속성 컨텍스트의 쓰기 지연 sql 저장소에 sql을 저장하고 스냅샷을 저장하지만, em.merge()의 경우에는 DB에서 select문으로 데이터를 가져와 save를 하기 때문에 비효율적으로 동작하게 된다.
+ 💡 참고) 그렇다면 @Transactional 어노테이션을 붙인 상태에서 save()메서드를 반복 호출하면 어떻게 될까?
@Test @Transactional fun saveWithTransactional() { val users = mutableListOf<UserEntity>() for (i in 1..1000) { users.add(UserEntity(email = "testUser$i@gmail.com", name = "testUser$i", phone = "010-1234-567$i", status = UserStatus.ACTIVE)) } val start = System.currentTimeMillis() for (user in users) { userRepository.save(user) } val end = System.currentTimeMillis() println("Time taken to save 1000 users: ${end - start} ms") }
트랜잭션이 열린뒤 한번에 insert query가 반복 실행되고, 마지막에 commit하는 것을 확인할 수 있다.
(여기서는 테스트 코드에 그대로 붙였기 때문에 rollback으로 표시되었다)
Mon Apr 07 23:03:50 KST 2025 INFO: [QUERY] SET autocommit=0 [Created on: Mon Apr 07 23:03:50 KST 2025, duration: 2, connection-id: 72, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)] Mon Apr 07 23:03:50 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:50 KST 2025, duration: 0, connection-id: 72, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)] WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information WARNING: Dynamic loading of agents will be disallowed by default in a future release Mon Apr 07 23:03:50 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:03:50.907361','testUser1@gmail.com','testUser1','010-1234-5671','ACTIVE','2025-04-07 23:03:50.907396') [Created on: Mon Apr 07 23:03:50 KST 2025, duration: 5, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:50 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:50 KST 2025, duration: 0, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:03:50.907422','testUser2@gmail.com','testUser2','010-1234-5672','ACTIVE','2025-04-07 23:03:50.907429') [Created on: Mon Apr 07 23:03:50 KST 2025, duration: 1, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 0, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:03:50.90744','testUser3@gmail.com','testUser3','010-1234-5673','ACTIVE','2025-04-07 23:03:50.907445') [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 1, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 0, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:03:50.907455','testUser4@gmail.com','testUser4','010-1234-5674','ACTIVE','2025-04-07 23:03:50.907478') [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 1, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 0, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:03:50.907501','testUser5@gmail.com','testUser5','010-1234-5675','ACTIVE','2025-04-07 23:03:50.907507') [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 2, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 0, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:03:50.907518','testUser6@gmail.com','testUser6','010-1234-5676','ACTIVE','2025-04-07 23:03:50.907522') [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 1, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 0, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:03:50.907529','testUser7@gmail.com','testUser7','010-1234-5677','ACTIVE','2025-04-07 23:03:50.907532') [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 1, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 0, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:03:50.90754','testUser8@gmail.com','testUser8','010-1234-5678','ACTIVE','2025-04-07 23:03:50.907545') [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 2, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] Mon Apr 07 23:03:51 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:51 KST 2025, duration: 0, connection-id: 72, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)] ... Mon Apr 07 23:03:55 KST 2025 INFO: [QUERY] ROLLBACK [Created on: Mon Apr 07 23:03:55 KST 2025, duration: 17, connection-id: 72, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.rollback(ProxyConnection.java:386)] Mon Apr 07 23:03:55 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:55 KST 2025, duration: 0, connection-id: 72, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.rollback(ProxyConnection.java:386)] Mon Apr 07 23:03:55 KST 2025 INFO: [QUERY] SET autocommit=1 [Created on: Mon Apr 07 23:03:55 KST 2025, duration: 1, connection-id: 72, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)] Mon Apr 07 23:03:55 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:03:55 KST 2025, duration: 0, connection-id: 72, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
실행 결과는 다음과 같았다.
연결을 맺는 시간이 줄었기 때문에 시간이 50% 이상 향상된것을 확인할 수 있다.
Time taken to save 1000 users: 4094 ms
2. JPA의 saveAll() 메서드 활용
이번엔 JPA Repository의 saveAll을 활용해보자.
@Test
fun saveAll() {
val users = mutableListOf<UserEntity>()
for (i in 1..1000) {
users.add(UserEntity(email = "testUser$i@gmail.com", name = "testUser$i", phone = "010-1234-567$i", status = UserStatus.ACTIVE))
}
val start = System.currentTimeMillis()
userRepository.saveAll(users)
val end = System.currentTimeMillis()
println("Time taken to save 1000 users: ${end - start} ms")
}
실행 결과는 다음과 같다.
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
Mon Apr 07 23:32:31 KST 2025 INFO: [QUERY] SET autocommit=0 [Created on: Mon Apr 07 23:32:31 KST 2025, duration: 2, connection-id: 112, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 23:32:31 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:32:31 KST 2025, duration: 0, connection-id: 112, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 23:32:31 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:32:31.017575','testUser1@gmail.com','testUser1','010-1234-5671','ACTIVE','2025-04-07 23:32:31.017592') [Created on: Mon Apr 07 23:32:31 KST 2025, duration: 5, connection-id: 112, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
...
Mon Apr 07 23:32:34 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:32:34 KST 2025, duration: 0, connection-id: 112, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
Mon Apr 07 23:32:34 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:32:31.023324','testUser999@gmail.com','testUser999','010-1234-567999','ACTIVE','2025-04-07 23:32:31.023326') [Created on: Mon Apr 07 23:32:34 KST 2025, duration: 2, connection-id: 112, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
Mon Apr 07 23:32:34 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:32:34 KST 2025, duration: 0, connection-id: 112, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
Mon Apr 07 23:32:34 KST 2025 INFO: [QUERY] insert into user_entity (created_at,email,name,phone,status,updated_at) values ('2025-04-07 23:32:31.023328','testUser1000@gmail.com','testUser1000','010-1234-5671000','ACTIVE','2025-04-07 23:32:31.023329') [Created on: Mon Apr 07 23:32:34 KST 2025, duration: 3, connection-id: 112, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
Mon Apr 07 23:32:34 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:32:34 KST 2025, duration: 0, connection-id: 112, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)]
Mon Apr 07 23:32:34 KST 2025 INFO: [QUERY] COMMIT [Created on: Mon Apr 07 23:32:34 KST 2025, duration: 12, connection-id: 112, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Mon Apr 07 23:32:34 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:32:34 KST 2025, duration: 0, connection-id: 112, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Mon Apr 07 23:32:34 KST 2025 INFO: [QUERY] SET autocommit=1 [Created on: Mon Apr 07 23:32:34 KST 2025, duration: 2, connection-id: 112, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 23:32:34 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:32:34 KST 2025, duration: 0, connection-id: 112, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Time taken to save 1000 users: 3821 ms
saveAll의 메서드는 다음과 같이 내부에서 transactional을 생성하여 커넥션 안에서 반복 요청 하기 때문에 개별 save() 보다 빠른 결과를 낸다.
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
@Override
@Transactional
public <S extends T> List<S> saveAll(Iterable<S> entities) {
Assert.notNull(entities, ENTITIES_MUST_NOT_BE_NULL);
List<S> result = new ArrayList<>();
for (S entity : entities) {
result.add(save(entity));
}
return result;
}
}
3. JdbcTemplate의 batchUpdate 활용
이번엔 jdbcTemplate을 사용해서 직접 bulk insert를 구현해보자.
다음과 같이 jdbc를 사용해 bulk insert를 구현해준다.
@Component
class UserRepositoryImpl(
private val jdbcTemplate: JdbcTemplate
) : UserCustomRepository {
@Transactional
override fun saveAllWithBulkInsert(users: List<UserEntity>) {
if (users.isEmpty()) return
val sql = """
INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
""".trimIndent()
jdbcTemplate.batchUpdate(sql, users, users.size) { ps, user ->
ps.setString(1, user.email)
ps.setString(2, user.name)
ps.setString(3, user.phone)
ps.setString(4, user.status.name)
ps.setTimestamp(5, Timestamp.valueOf(user.createdAt))
ps.setTimestamp(6, Timestamp.valueOf(user.updatedAt))
}
}
}
@Test
fun bulk_insert() {
val users = mutableListOf<UserEntity>()
for (i in 1..1000) {
users.add(UserEntity(email = "testUser$i@gmail.com", name = "testUser$i", phone = "010-1234-567$i", status = UserStatus.ACTIVE))
}
val start = System.currentTimeMillis()
userRepository.saveAllWithBulkInsert(users)
val end = System.currentTimeMillis()
// Time taken to save 1000 users: 2419 ms
println("Time taken to save 1000 users: ${end - start} ms")
}
실행된 결과는 다음과 같다.
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
Mon Apr 07 23:59:18 KST 2025 INFO: [QUERY] SET autocommit=0 [Created on: Mon Apr 07 23:59:18 KST 2025, duration: 1, connection-id: 432, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 23:59:18 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:59:18 KST 2025, duration: 0, connection-id: 432, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
2025-04-07T23:59:18.147+09:00 DEBUG 6250 --- [ Test worker] o.s.jdbc.core.JdbcTemplate : Executing SQL batch update [INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)] with a batch size of 1000
2025-04-07T23:59:18.149+09:00 DEBUG 6250 --- [ Test worker] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)]
2025-04-07T23:59:18.154+09:00 DEBUG 6250 --- [ Test worker] o.s.jdbc.support.JdbcUtils : JDBC driver supports batch updates
Mon Apr 07 23:59:18 KST 2025 INFO: [QUERY] INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES ('testUser1@gmail.com', 'testUser1', '010-1234-5671', 'ACTIVE', '2025-04-07 23:59:18.113384', '2025-04-07 23:59:18.113399') [Created on: Mon Apr 07 23:59:18 KST 2025, duration: 4, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
...
Mon Apr 07 23:59:20 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 0, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [QUERY] INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES ('testUser996@gmail.com', 'testUser996', '010-1234-567996', 'ACTIVE', '2025-04-07 23:59:18.117981', '2025-04-07 23:59:18.117982') [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 1, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 0, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [QUERY] INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES ('testUser997@gmail.com', 'testUser997', '010-1234-567997', 'ACTIVE', '2025-04-07 23:59:18.117983', '2025-04-07 23:59:18.117984') [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 1, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 0, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [QUERY] INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES ('testUser998@gmail.com', 'testUser998', '010-1234-567998', 'ACTIVE', '2025-04-07 23:59:18.117985', '2025-04-07 23:59:18.117986') [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 1, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 0, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [QUERY] INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES ('testUser999@gmail.com', 'testUser999', '010-1234-567999', 'ACTIVE', '2025-04-07 23:59:18.117987', '2025-04-07 23:59:18.117988') [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 1, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 0, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [QUERY] INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES ('testUser1000@gmail.com', 'testUser1000', '010-1234-5671000', 'ACTIVE', '2025-04-07 23:59:18.117989', '2025-04-07 23:59:18.117991') [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 1, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 0, connection-id: 432, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Mon Apr 07 23:59:20 KST 2025 INFO: [QUERY] COMMIT [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 5, connection-id: 432, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Mon Apr 07 23:59:20 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 0, connection-id: 432, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Mon Apr 07 23:59:20 KST 2025 INFO: [QUERY] SET autocommit=1 [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 2, connection-id: 432, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Mon Apr 07 23:59:20 KST 2025 INFO: [FETCH] [Created on: Mon Apr 07 23:59:20 KST 2025, duration: 0, connection-id: 432, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Time taken to save 1000 users: 2419 ms
왜 실행된 쿼리는 비슷한데 JPA의 saveAll() 보다 빠른 속도가 나왔을까?
영속성 컨텍스트에 저장하는 과정이 생략되기 때문이다.
- saveAll()은 EntityManager.persist(...)를 호출하면서 엔티티를 영속성 컨텍스트에 등록
- 이 말은 즉, 엔티티의 상태를 추적하고 변경 감지까지 할 준비를 함
- 이게 메모리와 성능 모두에 부담
4. JdbcTemplate의 batchUpdate를 이용한 bulk insert (rewriteBatchedStatements=true)
이번에 rewriteBatchedStatements옵션을 활용하여 bulk insert를 이용해보자.
rewriteBatchedStatements=true는 MySQL JDBC 드라이버의 성능 최적화 옵션 중 하나로, JdbcTemplate.batchUpdate() 사용 시 실제 배치 insert 성능에 큰 영향을 미칠 수 있다.
rewriteBatchedStatements 옵션은 MySQL JDBC 드라이버가 addBatch()로 쌓은 SQL들을 하나의 multi-row insert로 재작성해주는 기능이다. 즉, DB로 전송되는 쿼리 수 자체가 줄어들어 성능이 폭발적으로 향상될 수 있다.
-- 일반 배치 insert (옵션 없음, 3개 쿼리 전송)
INSERT INTO user (...) VALUES (...);
INSERT INTO user (...) VALUES (...);
INSERT INTO user (...) VALUES (...);
-- rewriteBatchedStatements=true 설정 시
INSERT INTO user (...) VALUES (...), (...), (...);
jdbc url 에 다음과 같이 옵션을 추가해준다.
url: jdbc:mysql://localhost:33306/bulk_insert?profileSQL=true&rewriteBatchedStatements=true
@Test
fun bulk_insertWithRewriteBatchedStatementsOption() {
val users = mutableListOf<UserEntity>()
for (i in 1..1000) {
users.add(UserEntity(email = "testUser$i@gmail.com", name = "testUser$i", phone = "010-1234-567$i", status = UserStatus.ACTIVE))
}
val start = System.currentTimeMillis()
userRepository.saveAllWithBulkInsert(users)
val end = System.currentTimeMillis()
println("Time taken to save 1000 users: ${end - start} ms")
}
실행된 결과를 보면 다음과 같이 단 한줄의 쿼리로 실행이 완료된 것을 확인할 수 있다.
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
Tue Apr 08 00:19:50 KST 2025 INFO: [QUERY] SET autocommit=0 [Created on: Tue Apr 08 00:19:50 KST 2025, duration: 1, connection-id: 472, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Tue Apr 08 00:19:50 KST 2025 INFO: [FETCH] [Created on: Tue Apr 08 00:19:50 KST 2025, duration: 0, connection-id: 472, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
2025-04-08T00:19:50.681+09:00 DEBUG 6556 --- [ Test worker] o.s.jdbc.core.JdbcTemplate : Executing SQL batch update [INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)] with a batch size of 1000
2025-04-08T00:19:50.682+09:00 DEBUG 6556 --- [ Test worker] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)]
2025-04-08T00:19:50.688+09:00 DEBUG 6556 --- [ Test worker] o.s.jdbc.support.JdbcUtils : JDBC driver supports batch updates
Tue Apr 08 00:19:50 KST 2025 INFO: [QUERY] INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES ('testUser1@gmail.com', 'testUser1', '010-1234-5671', 'ACTIVE', '2025-04-08 00:19:50.649125', '2025-04-08 00:19:50.649144'),('testUser2@gmail.com', 'testUser2', '010-1234-5672', 'ACTIVE', '2025-04-08 00:19:50.649159', '2025-04-08 00:19:50.649164'),('testUser3@gmail.com', 'testUser3', '010-1234-5673', 'ACTIVE', '2025-04-08 00:19:50.64917', '2025-04-08 00:19:50.649172'),('testUser4@gmail.com', 'testUser4', '010-1234-5674', 'ACTIVE', '2025-04-08 00:19:50.649177', '2025-04-08 00:19:50.64918'),('testUser5@gmail.com', 'testUser5', '010-1234-5675', 'ACTIVE', '2025-04-08 00:19:50.649184', '2025-04-08 00:19:50.649187'),('testUser6@gmail.com', 'testUser6', '010-1234-5676', 'ACTIVE', '2025-04-08 00:19:50.649192', '2025-04-08 00:19:50.649194'),('testUser7@gmail.com', 'testUser7', '010-1234-5677', 'ACTIVE', '2025-04-08 00:19:50.649198', '2025-04-08 00:19:50.649201'),('testUser8@gmail.com', 'testUser8', '010-1234-5678', 'ACTIVE', '2025-04-08 00:19:50.649207', '2025-04-08 00:19:50.64921'),('testUser9@gmail.com', 'testUser9', '010-1234-5679', 'ACTIVE', '2025-04-08 00:19:50.649215', '2025-04-08 00:19:50.649217'),('testUser10@gmail.com', 'testUser10', '010-1234-56710', 'ACTIVE', '2025-04-08 00:19:50.649222', '2025-04-08 00:19:50.649224'),('testUser11@gmail.com', 'testUser11', '010-1234-56711', 'ACTIVE', '2025-04-08 00:19:50.649229', '2025-04-08 00:19:50.649232'),('testUser12@gmail.com', 'testUser12', '010-1234-56712', 'ACTIVE', '2025-04-08 00:19:50.649237', '2025-04-08 00:19:50.649239'),('testUser13@gmail.com', 'testUser13', '010-1234-56713', 'ACTIVE', '2025-04-08 00:19:50.649243', '2025-04-08 00:19:50.649246'),('testUser14@gmail.com', 'testUser14', '010-1234-56714', 'ACTIVE', '2025-04-08 00:19:50.64925', '2025-04-08 00:19:50.649253'),('testUser15@gmail.com', 'testUser15', '010-1234-56715', 'ACTIVE', '2025-04-08 00:19:50.649258', '2025-04-08 00:19:50.64926'),('testUser16@gmail.com', 'testUser16', '010-1234-56716', 'ACTIVE', '2025-04-08 00:19:50.649265', ' ... (truncated) [Created on: Tue Apr 08 00:19:50 KST 2025, duration: 40, connection-id: 472, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Tue Apr 08 00:19:50 KST 2025 INFO: [FETCH] [Created on: Tue Apr 08 00:19:50 KST 2025, duration: 0, connection-id: 472, statement-id: 0, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:127)]
Tue Apr 08 00:19:50 KST 2025 INFO: [QUERY] COMMIT [Created on: Tue Apr 08 00:19:50 KST 2025, duration: 5, connection-id: 472, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Tue Apr 08 00:19:50 KST 2025 INFO: [FETCH] [Created on: Tue Apr 08 00:19:50 KST 2025, duration: 0, connection-id: 472, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)]
Tue Apr 08 00:19:50 KST 2025 INFO: [QUERY] SET autocommit=1 [Created on: Tue Apr 08 00:19:50 KST 2025, duration: 1, connection-id: 472, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
Tue Apr 08 00:19:50 KST 2025 INFO: [FETCH] [Created on: Tue Apr 08 00:19:50 KST 2025, duration: 0, connection-id: 472, statement-id: -1, resultset-id: 0, at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:402)]
실행 경과 시간은 다음과 같다.
Time taken to save 1000 users: 201 ms
처음에 비하면 엄청난 속도향상을 보였다!
또한, DB 팀에게 bulk insert 를 사용하기를 권유받았는데, 이는 단순히 성능 향상 이상의 DB 안정성 향상, 리소스 절약, 동시성 개선까지 수행하기 때문이다.
rewriteBatchedStatements | ❌ | ✅ |
DB 처리 건수 | SQL N개 | SQL 1개 (multi-row) |
Network Round Trip | N회 | 1회 |
쿼리 파싱/컴파일 부하 | N번 반복 | 1번 |
락 충돌 위험 | 높음 | 낮음 |
Disk I/O 및 트랜잭션 로그 부하 | 높음 | 낮음 |
전체 시스템 자원 소모 | 큼 | 현저히 적음 ✅ |
1. Jdbc의 batchUpdate를 활용하면 한줄의 sql로 여러줄의 insert가 가능하다.
- 드라이버 수준에서 PreparedStatement를 재사용해서 DB에 전달
- insert SQL을 1개만 컴파일해서 여러 데이터로 반복 실행
- DB에 1회 네트워크 round trip에 여러 row 전송 가능
2. 쿼리 파싱/컴파일 부하 감소
- 단 한 번의 SQL → 한 번만 컴파일하면 됨
- 쿼리 파싱/컴파일은 CPU 부하와 메모리 캐시 오염의 주범
- 특히 많은 연결이 동시에 insert할 경우 차이가 큼
3. 네트워크 부하
- 데이터가 묶여 한 번만 전송 → 네트워크 왕복 횟수 줄어듬
- 특히 DB와 WAS가 다른 서버에 있을 경우 효과 매우 큼
4. 트랜잭션 로그 (Disk I/O) 및 락 충돌
- 옵션 미사용시
- row 하나 삽입마다 디스크에 쓰기
- 오토 커밋 ON이면 매 줄마다 트랜잭션 커밋 → 디스크 flush
- 동일 테이블에 다중 삽입 시 락 충돌 위험 높음
- 옵션 사용시
- 한번에 삽입되므로 로그 write도 압축되고 효율적
- 락도 짧게, 적게 걸림 → 동시성 부하 줄어듦
5. Buffer Pool / Query Cache 효율
- 옵션 미사용시
- 각기 다른 SQL로 인식됨 → query cache miss 많아짐
- 옵션 사용시
- 하나의 쿼리로 재사용됨 → query cache hit 유리
💡 참고) mariadb connector j를 사용하는 경우 -> 버전 확인 필요
나의 경우 mariadb java client를 사용하고 있었는데, 다른 팀의 동일한 라이브러리에서는 profileSQL, rewriteBatchedStatements 옵션이 정상 동작하는데, 우리 팀의 프로젝트에서는 아무리 옵션을 적용해도 동작하지 않았다.
그래서 설치된 external 라이브러리 버전을 보니, 다른 팀의 경우 2.7.X 버전을, 우리 팀의 레포는 3.1.X 버전이 설치되는 것을 확인했다. (이전 버전을 명시하려고 해도 디펜던시 호환 떄문인지 이전버전으로 설치가 안되었다.)
그래서 공식 문서를 찾아보니,
링크 : https://mariadb.com/kb/en/about-mariadb-connector-j/#:~:text=rewriteBatchedStatements
About MariaDB Connector/J
LGPL-licensed MariaDB client library for Java applications.
mariadb.com
두가지 옵션이 mariadb-java-client 3.X 버전부터 모두 removed 되었다.
이번엔 다음과 같이 mariadb java client를 설치해서 테스트 해보았다.
plugins {
kotlin("jvm") version "1.9.25"
kotlin("plugin.spring") version "1.9.25"
id("org.springframework.boot") version "3.4.4"
id("io.spring.dependency-management") version "1.1.7"
kotlin("kapt") version "1.9.21"
kotlin("plugin.jpa") version "1.9.21"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.mariadb.jdbc:mariadb-java-client")
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
kapt("com.querydsl:querydsl-apt:5.0.0:jakarta")
}
mariadb-java-client 3점대 버전이 생성되는 것을 확인하였다.
spring:
jpa:
properties:
default_batch_fetch_size: 500
org.hibernate.flushMode: COMMIT
hibernate:
format_sql: false
query:
in_clause_parameter_padding: true
plan_cache_max_size: 128
plan_parameter_metadata_max_size: 16
hibernate:
ddl-auto: validate
show-sql: false
datasource:
driver-class-name: org.mariadb.jdbc.Driver
// 옵션 삭제
url: jdbc:mariadb://localhost:33306/bulk_insert
username: root
password: root
hikari:
connection-timeout: 5000
max-lifetime: 58000
leak-detection-threshold: 30000
maximum-pool-size: 40
logging:
level:
root: info
org:
springframework:
jdbc: debug
mariadb:
jdbc: debug
다음과 같이 건건별로 쿼리가 날라가는 것을 확인할 수 있다.
2025-04-08T01:02:17.273+09:00 DEBUG 7591 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: set autocommit=0
2025-04-08T01:02:17.274+09:00 DEBUG 7591 --- [ Test worker] o.mariadb.jdbc.message.server.OkPacket : System variable change: autocommit = OFF
2025-04-08T01:02:17.282+09:00 DEBUG 7591 --- [ Test worker] o.s.jdbc.core.JdbcTemplate : Executing SQL batch update [INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)] with a batch size of 1000
2025-04-08T01:02:17.285+09:00 DEBUG 7591 --- [ Test worker] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)]
2025-04-08T01:02:17.288+09:00 DEBUG 7591 --- [ Test worker] o.s.jdbc.support.JdbcUtils : JDBC driver supports batch updates
2025-04-08T01:02:17.304+09:00 DEBUG 7591 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
2025-04-08T01:02:17.305+09:00 DEBUG 7591 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
...
2025-04-08T01:02:17.674+09:00 DEBUG 7591 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: INSERT INTO user (email, name, phone, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
2025-04-08T01:02:17.705+09:00 DEBUG 7591 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: COMMIT
2025-04-08T01:02:17.713+09:00 DEBUG 7591 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: set autocommit=1
2025-04-08T01:02:17.714+09:00 DEBUG 7591 --- [ Test worker] o.mariadb.jdbc.message.server.OkPacket : System variable change: autocommit = ON
Time taken to save 1000 users: 466 ms
그렇다면 rewriteBatchedStatements를 적용한 것 처럼 단건의 쿼리로 나가게하려면 어떻게 할 수 있을까?
이제 수정해서 하나의 쿼리로 나갈 수 있게 해보자.
@Transactional
override fun saveAllAsMultiRow(users: List<UserEntity>) {
if (users.isEmpty()) return
val sqlPrefix = "INSERT INTO user (email, name, phone, status, created_at, updated_at) VALUES "
val rowPlaceholders = List(users.size) { "(?, ?, ?, ?, ?, ?)" }.joinToString(", ")
val finalSql = sqlPrefix + rowPlaceholders
val params = mutableListOf<Any>()
for (user in users) {
params += user.email
params += user.name
params += user.phone
params += user.status.name
params += Timestamp.valueOf(user.createdAt)
params += Timestamp.valueOf(user.updatedAt)
}
jdbcTemplate.update(finalSql, *params.toTypedArray())
}
다시 테스트 해보면
@Test
fun bulk_insertWithMariaDBJavaClient() {
val users = mutableListOf<UserEntity>()
for (i in 1..1000) {
users.add(UserEntity(email = "testUser$i@gmail.com", name = "testUser$i", phone = "010-1234-567$i", status = UserStatus.ACTIVE))
}
val start = System.currentTimeMillis()
userRepository.saveAllAsMultiRow(users)
// Time taken to save 1000 users: 174 ms
val end = System.currentTimeMillis()
println("Time taken to save 1000 users: ${end - start} ms")
}
원하는대로 쿼리가 한줄로 실행되는 것을 확인할 수 있다.
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
2025-04-08T01:31:25.157+09:00 DEBUG 8478 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: set autocommit=0
2025-04-08T01:31:25.159+09:00 DEBUG 8478 --- [ Test worker] o.mariadb.jdbc.message.server.OkPacket : System variable change: autocommit = OFF
2025-04-08T01:31:25.189+09:00 DEBUG 8478 --- [ Test worker] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL update
2025-04-08T01:31:25.190+09:00 DEBUG 8478 --- [ Test worker] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [INSERT INTO user (email, name, phone, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?)]
2025-04-08T01:31:25.208+09:00 DEBUG 8478 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: INSERT INTO user (email, name, phone, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?)
2025-04-08T01:31:25.292+09:00 DEBUG 8478 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: COMMIT
2025-04-08T01:31:25.299+09:00 DEBUG 8478 --- [ Test worker] o.m.jdbc.client.impl.StandardClient : execute query: set autocommit=1
2025-04-08T01:31:25.300+09:00 DEBUG 8478 --- [ Test worker] o.mariadb.jdbc.message.server.OkPacket : System variable change: autocommit = ON
Time taken to save 1000 users: 174 ms
끝-!! ✨✨✨✨✨✨✨✨
참고)
[JPA] Spring JPA 환경에서 bulk insert를 효율적으로 해보자 - JPA의 한계와 JDBC 활용
안녕하세요. 오늘은 Spring JPA 환경에서 bulk insert를 효율적으로 하는 방법에 대해서 알아보는 시간을 가져보도록 하겠습니다. JPA를 사용하고 ID를 전략을 사용했을 때 JPA의 성능 문제는 이전에 [JPA
sabarada.tistory.com
Spring JPA Save() vs SaveAll() vs Bulk Insert
MySQL에 대용량 데이터를 삽입해야 할 일이 발생했습니다. 기존에 JPA를 사용하여 데이터를 저장하는 메서드인 Save()와 SaveAll()의 차이에 대해서 알아보고 위 2가지의 단건 삽입과 반대대는 Bulk 삽
velog.io
- https://dct-wonjung.tistory.com/entry/JPA-mysql-saveall-Bulk-Insert
[JPA/MySQL] saveAll() 쓰면 쿼리 하나로 나가는 거 아니었어? / JPA에서 Bulk Insert 처리해보기
Bulk Insert란? INSERT 쿼리를 한번에 처리하는 것 MySQL에서는 아래처럼 Insert 합치기 옵션을 통해 성능을 비약적으로 향상할 수 있다. INSERT INTO person (name) VALUES ('name1'), ('name2'), ('name3'); Hibernate의 Bulk In
dct-wonjung.tistory.com
JPA ID전략이 IDENTITY인 상태에서 Bulk Insert 수행하기
Bulk Insert
medium.com
- https://sabarada.tistory.com/220
[JPA] Spring JPA 환경에서 bulk insert를 효율적으로 해보자 - JPA의 한계와 JDBC 활용
안녕하세요. 오늘은 Spring JPA 환경에서 bulk insert를 효율적으로 하는 방법에 대해서 알아보는 시간을 가져보도록 하겠습니다. JPA를 사용하고 ID를 전략을 사용했을 때 JPA의 성능 문제는 이전에 [JPA
sabarada.tistory.com
- https://starting-coding.tistory.com/695
대량 쿠폰 발급 시 Bulk Insert로 처리 시간 70% 단축
대량 쿠폰 발급 시 Bulk Insert로 처리 시간 70% 단축쿠폰 발급과 관련된 프로덕션 코드를 작성하면서,향후 서비스 규모가 커짐에 따라 쿠폰을 발급하는 대상 회원 수가 100명, 1,000명, 혹은 10,000명이
starting-coding.tistory.com
- https://sabarada.tistory.com/220
[JPA] Spring JPA 환경에서 bulk insert를 효율적으로 해보자 - JPA의 한계와 JDBC 활용
안녕하세요. 오늘은 Spring JPA 환경에서 bulk insert를 효율적으로 하는 방법에 대해서 알아보는 시간을 가져보도록 하겠습니다. JPA를 사용하고 ID를 전략을 사용했을 때 JPA의 성능 문제는 이전에 [JPA
sabarada.tistory.com
- https://dct-wonjung.tistory.com/entry/JPA-mysql-saveall-Bulk-Insert
[JPA/MySQL] saveAll() 쓰면 쿼리 하나로 나가는 거 아니었어? / JPA에서 Bulk Insert 처리해보기
Bulk Insert란? INSERT 쿼리를 한번에 처리하는 것 MySQL에서는 아래처럼 Insert 합치기 옵션을 통해 성능을 비약적으로 향상할 수 있다. INSERT INTO person (name) VALUES ('name1'), ('name2'), ('name3'); Hibernate의 Bulk In
dct-wonjung.tistory.com
JPA ID전략이 IDENTITY인 상태에서 Bulk Insert 수행하기
Bulk Insert
medium.com
- https://joojimin.tistory.com/71
Spring Data JPA @Modifying 알아보기
벌크 연산 관련 공부를 하다가 @Modifying에 대해 자세히 알아볼 필요가 생겨 공부한 내용입니다 https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/Modifying.html Modifying (Spri
joojimin.tistory.com
- https://netal.tistory.com/entry/a
[DB] JPA의 orphanremoval 또는 CascadeType.REMOVE 에서 발생할 수 있는 성능 문제와 버그(?)
이번에 프로젝트를 진행하며, orphanremoval = true 로 엔티티 삭제를 구현해볼까? 라는 생각이 들었습니다. 그런데 실제로 적용해보고 쿼리가 나가는 것을 확인해보며 이런저런 문제가 있다는 것을
netal.tistory.com
- JPA에서 modifying을 이용한 연산
https://joojimin.tistory.com/71
Spring Data JPA @Modifying 알아보기
벌크 연산 관련 공부를 하다가 @Modifying에 대해 자세히 알아볼 필요가 생겨 공부한 내용입니다 https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/Modifying.html Modifying (Spri
joojimin.tistory.com