JdbcTemplate batchUpdate()

아이스__아메리·2025년 1월 10일

JdbcTemplate

목록 보기
1/1

JdbcTemplate에서 batchUpdate() 사용하는 주 목적은 성능이다.

내가 주로 사용하고 있는 JPA saveAll()은 대체로 작은 데이터셋에서는 사용에 적합하다. 엔티티를 관리하는데 드는 비용이 크기 때문에 큰 데이터셋을 처리할 때는 성능 문제가 발생할 수 있다.

batchUpdate()는 사용하여 원시 SQL을 사용하여 엔티티를 사용하는 JPA보다 더 낮은 오버헤드를 제공한다. 따라서 성능이 매우 높고, 대규모 데이터를 처리할 때 JPA보다 더 적합하다.

10만건을 기준으로 처리 시간 차이이다. 테이블 인덱스나 데이터양의 차이에 따라 JPA는 시간적으로 편차가있으나 JdbcTemplate의 속도에 미치지 못한다.

비즈니스 로직

@Transactional
public Object batchUpdate(String userId, Request request) {
	String sql = "  UPDATE central_cities SET is_central = ?, modified = GETDATE(), modified_by = ? " +
			"       WHERE country_id = ? AND city_id = ?";

	int[][] updateCounts;
	try {
		updateCounts = this.jdbcTemplate.batchUpdate(sql, request.getCities(), 100, (ps, city) -> {
			ps.setBoolean(1, city.getIsCentral());
			ps.setString(2, userId);
			ps.setString(3, city.getCountryId());
			ps.setString(4, city.getCityId());
		});

	} catch (Exception e) {
		throw new RuntimeException("Failed to update cities: " + e.getMessage(), e);
	}

	// 성공 결과 반환
	Map<String, Object> result = new HashMap<>();
	result.put("totalUpdates", updateCounts.length);
	result.put("success", Arrays.stream(updateCounts).findAny().isPresent());

	return result;
}

DTO

@Getter
public class Request {
    private List<City> cities;

    @Getter
    public static class City {
        private String countryId;
        private String cityId;
        private Boolean isCentral;
    }
}

요청 예시


SQL문을 한 번만 파싱하고 재사용한다.

-- PreparedStatement가 내부적으로 처리하는 방식
PREPARE stmt FROM 'UPDATE central_cities SET is_central = ?, modified = GETDATE(), modified_by = ? WHERE country_id = ? AND city_id = ?';

-- 배치로 파라미터 바인딩 후 실행
EXECUTE stmt USING true, 'admin001', 'KR', 'SEOUL';
EXECUTE stmt USING false, 'admin001', 'KR', 'BUSAN';
-- ... 100개 실행

DEALLOCATE PREPARE stmt;

예시는 Update를 다뤘지만 Insert, Delete에 대해서도 가능하다.

Insert의 경우 비교

-- JPA saveAll
INSERT INTO central_cities VALUES ('KR', 'SEOUL', true, GETDATE(), 'admin001');
INSERT INTO central_cities VALUES ('KR', 'BUSAN', false, GETDATE(), 'admin001');

-- Batch Insert
INSERT INTO central_cities VALUES 
  ('KR', 'SEOUL', true, GETDATE(), 'admin001'),
  ('KR', 'BUSAN', false, GETDATE(), 'admin001')

SQL의 Batch Insert를 Spring에서 사용한다고 보면된다.

마무리

JPA를 주로 사용한 프로젝트에서 대용량 데이터가 다루는 부분에서는 사용을 고려해볼만하다.

profile
츠케멘 좋아

0개의 댓글