Spring

Spring JPA의 save() vs saveAll() vs bulk insert() (feat. db client)

dodop 2025. 4. 8. 01:37

 

 

 

이번에 면허 재검증 프로세스를 구성하면서 면허 검증 로깅에 관한 작업을 구성하게 되었다. 

면허 검증에는 배치로 검증하는 과정이 있기 떄문에, 로깅도 한번에 저장되어 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를 실행해야 한다. 

참고 : https://stackoverflow.com/questions/27697810/why-does-hibernate-disable-insert-batching-when-using-an-identity-identifier-gen/27732138#27732138

 

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

- https://medium.com/@hee98.09.14/jpa-id%EC%A0%84%EB%9E%B5%EC%9D%B4-identity%EC%9D%B8-%EC%83%81%ED%83%9C%EC%97%90%EC%84%9C-bulk-insert-%EC%88%98%ED%96%89%ED%95%98%EA%B8%B0-8bf9c760bd82

 

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

- https://medium.com/@hee98.09.14/jpa-id%EC%A0%84%EB%9E%B5%EC%9D%B4-identity%EC%9D%B8-%EC%83%81%ED%83%9C%EC%97%90%EC%84%9C-bulk-insert-%EC%88%98%ED%96%89%ED%95%98%EA%B8%B0-8bf9c760bd82

 

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