🏗️ Build Up
💨What
❓Why
✅How
- file structure

- Repository Code
package com.kdt.instakyuram.article.postimage.domain; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class PostImageCustomRepositoryImpl implements PostImageCustomRepository { private final int DEFAULT_BATCH_SIZE = 5; private final JdbcTemplate jdbcTemplate; public PostImageCustomRepositoryImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void batchSaveAll(List<PostImage> postImages) { final String sql = "INSERT INTO post_image " + "(post_id, original_file_name, server_file_name, size, path) " + "VALUES (?, ?, ?, ?, ?)"; List<PostImage> batchs = new ArrayList<>(); for (PostImage postImage : postImages) { batchs.add(postImage); if (batchs.size() % DEFAULT_BATCH_SIZE == 0) { this.insertBulk(batchs, sql); } } if (!batchs.isEmpty()) { insertBulk(postImages, sql); } } private void insertBulk(List<PostImage> postImages, String sql) { jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setLong(1, postImages.get(i).getPost().getId()); ps.setString(2, postImages.get(i).getOriginalFileName()); ps.setString(3, postImages.get(i).getServerFileName()); ps.setLong(4, postImages.get(i).getSize()); ps.setString(5, postImages.get(i).getPath()); } @Override public int getBatchSize() { return postImages.size(); } } ); postImages.clear(); } }
- DB source 및 Test 설정
packagecom.kdt.instakyuram.article.postimage.domain; importjava.util.List; importjava.util.stream.Collectors; importjava.util.stream.IntStream; importjavax.sql.DataSource; importorg.junit.jupiter.api.DisplayName; importorg.junit.jupiter.api.Test; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.boot.jdbc.DataSourceBuilder; importorg.springframework.boot.test.context.TestConfiguration; importorg.springframework.context.annotation.Bean; importorg.springframework.jdbc.core.JdbcTemplate; importorg.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; importorg.springframework.test.context.junit.jupiter.SpringJUnitConfig; importcom.kdt.instakyuram.article.post.domain.Post; importcom.zaxxer.hikari.HikariDataSource; @SpringJUnitConfig(JdbcSource.class) classPostImageCustomRepositoryImplTest { @Autowired PostImageCustomRepositorypostImageCustomRepository; @Test @DisplayName("batch test") voidmakePostImages() { List<PostImage> postImages =IntStream.rangeClosed(1, 10) .mapToObj(count -> { String fileName = "test" + count; String path = "/post"; returnPostImage.builder() .post(Post.builder().id(1L).build()) .originalFileName(fileName) .serverFileName(fileName) .size(12345L) .path(path) .build(); }).collect(Collectors.toList()); postImageCustomRepository.batchSaveAll(postImages); } } @TestConfiguration classJdbcSource { @Bean publicDataSourcedataSource() { returnDataSourceBuilder .create() .url("jdbc:mysql://localhost:3306/kyuram?serverTimezone=Asia/Seoul&rewriteBatchedStatements=true&profileSQL=true&c=Slf4JLogger") .username("root") .password("password") .type(HikariDataSource.class) .build(); } @Bean publicNamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSourcedataSource) { return newNamedParameterJdbcTemplate(dataSource); } @Bean publicJdbcTemplate jdbcTemplate(DataSourcedataSource) { return newJdbcTemplate(dataSource); } @Bean publicPostImageCustomRepositorypostImageCustomRepository() { return newPostImageCustomRepositoryImpl(jdbcTemplate(dataSource())); } }
- 쿼리 콘솔 결과
- 5개씩 2번의 배치 - 총 10건

📌 REFER
- 외국 자료
- Batch Insert 성능 향상기 1,2
키전략
- 시퀀스 테이블 성능이슈까지 다뤄줌 ㅎ
- mybatis (쓰기지연 x)과 비교
- DataSource 설정