JPA에서 JdbcTemplate로 bulk update 최적화
원래 Spring Data JPA의 messageRepository.saveAll(messages)를 사용해 Message 객체 리스트를 데이터베이스에 저장했으나, saveAll이 개별 save를 반복하는 방식이라 대량 작업에 비효율적이라는 것을 알게 됐다.
당연히 saveAll은 벌크 업데이트일 줄 알았던 나는 큰 충격을 받았다. 리소스 관리를 위해 Redis를 쓰고 최적화에 노력했는데, 정작 saveAll로 많은 SQL 쿼리를 발생시키고 있었다. 그래서 JdbcTemplate를 이용해 벌크 업데이트를 할 수 있다는 것을 찾아내고 해결했다.
- JPA의 비효율성:
-
- 처음에 쓴
messageRepository.saveAll(messages)(JPA)는 각Message마다 별도의INSERT문을 보내 느렸고, 데이터베이스 왕복이 너무 많았다.
- 처음에 쓴
- JPA 인터페이스 한계:
-
MessageRepository가 JPA 인터페이스라JdbcTemplate를 직접 사용할 수 없었다. 인터페이스는 필드나 로직을 가질 수 없기 때문이다.
문제
- 새 클래스 생성:
-
JdbcTemplate로 벌크 삽입을 처리하기 위해 새 클래스MessageBulkRepository를 만들었다:@Repository public class MessageBulkRepository를 정의하고,JdbcTemplate필드를 추가 (@RequiredArgsConstructor로 주입).- 이 클래스는 JPA 리포지토리와 별도로 대량 삽입을 담당.
- bulk update 메서드:
-
bulkInsertMessages(List를messages) jdbcTemplate.batchUpdate로 구현.- SQL:
"INSERT INTO message (chat_room_id, sender_id, content, enrolled_at) VALUES (?, ?, ?, ?)"로 작성,message_id는 자동 증가라 제외. BatchPreparedStatementSetter사용:setValues에서 각Message객체의 필드를 SQL 플레이스홀더에 매핑.getBatchSize() { return messages.size(); }로 배치 크기 설정.
- 코드:
-
@Repository @RequiredArgsConstructor public class MessageBulkRepository { private final JdbcTemplate jdbcTemplate; public void bulkInsertMessages(Listmessages) { String sql = "INSERT INTO message (chat_room_id, sender_id, content, enrolled_at) " + "VALUES (?, ?, ?, ?)"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { Message message = messages.get(i); ps.setLong(1, message.getChatRoom().getChatRoomId()); if (message.getUser() != null) { ps.setLong(2, message.getUser().getUserId()); } else { ps.setNull(2, java.sql.Types.BIGINT); } ps.setString(3, message.getContent()); ps.setTimestamp(4, message.getEnrolledAt()); } @Override public int getBatchSize() { return messages.size(); } }); } }
해결한 방법
Posted on 2025.03.11