대부분의 JDBC 드라이버는 동일한 준비된 statement에 대해 여러 호출을 일괄 처리(batch)하는 경우 향상된 성능을 제공합니다. 업데이트를 일괄 처리로 그룹화하면 데이터베이스 왕복(round trips) 횟수를 제한할 수 있습니다.
JdbcTemplate특별한 인터페이스인 BatchPreparedStatementSetter의 두 가지 메소드를 구현하고 해당 구현을 batchUpdate 메소드 호출의 두 번째 매개변수로 전달하여 JdbcTemplate 일괄 처리를 수행합니다. getBatchSize 메소드를 사용하여 현재 배치의 크기를 제공할 수 있습니다. setValues 메소드를 사용하여 준비된 statement의 매개변수 값을 설정할 수 있습니다. 이 메소드는 getBatchSize 호출에서 지정한 횟수만큼 호출됩니다. 다음 예에서는 목록의 항목(entries)을 기반으로 t_actor 테이블을 업데이트하고 전체 목록이 배치로 사용됩니다.
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int[] batchUpdate(final List<Actor> actors) {
return this.jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
Actor actor = actors.get(i);
ps.setString(1, actor.getFirstName());
ps.setString(2, actor.getLastName());
ps.setLong(3, actor.getId().longValue());
}
public int getBatchSize() {
return actors.size();
}
});
}
// ... additional methods
}
업데이트 스트림을 처리하거나 파일에서 읽는 경우 선호하는 배치 크기가 있을 수 있지만 마지막 배치에는 해당 개수의 항목이 없을 수 있습니다. 이 경우 입력 소스가 소진되면 배치를 중단할 수 있는 InterruptibleBatchPreparedStatementSetter 인터페이스를 사용할 수 있습니다. isBatchExhausted 메소드를 사용하면 배치 종료를 알릴 수 있습니다.
JdbcTemplate과 NamedParameterJdbcTemplate은 모두 일괄 업데이트를 제공하는 대체 방법을 제공합니다. 특별한 배치 인터페이스를 구현하는 대신 호출의 모든 매개변수 값을 목록으로 제공합니다. 프레임워크는 이러한 값을 반복하고 내부 준비된 statement setter를 사용합니다. API는 명명된 매개변수 사용 여부에 따라 달라집니다. 명명된 매개 변수의 경우 일괄 처리의 각 구성원에 대해 하나의 항목인 SqlParameterSource 배열을 제공합니다. SqlParameterSourceUtils.createBatch 편의 메서드를 사용하여 이 배열을 생성하고 Bean 스타일 객체(매개변수에 해당하는 getter 메서드 포함), String-키 Map 인스턴스(해당 매개변수를 값으로 포함) 또는 둘 모두를 혼합하여 전달할 수 있습니다.
다음 예에서는 명명된 매개변수를 사용한 일괄 업데이트를 보여줍니다.
public class JdbcActorDao implements ActorDao {
private NamedParameterTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int[] batchUpdate(List<Actor> actors) {
return this.namedParameterJdbcTemplate.batchUpdate(
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
SqlParameterSourceUtils.createBatch(actors));
}
// ... additional methods
}
클래식 ? 자리 표시자를 사용하는 SQL 문의 경우 업데이트 값이 포함된 객체 배열이 포함된 목록을 전달합니다. 이 개체 배열에는 SQL 문의 각 자리 표시자에 대해 하나의 항목이 있어야 하며 SQL 문에 정의된 순서와 동일해야 합니다.
다음 예제는 클래식 JDBC ? 자리 표시자를 사용한다는 점을 제외하면 이전 예제와 동일합니다:
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int[] batchUpdate(final List<Actor> actors) {
List<Object[]> batch = new ArrayList<>();
for (Actor actor : actors) {
Object[] values = new Object[] {
actor.getFirstName(), actor.getLastName(), actor.getId()};
batch.add(values);
}
return this.jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
batch);
}
// ... additional methods
}
앞에서 설명한 모든 일괄 업데이트 메서드는 각 일괄 항목에 대해 영향을 받는 행 수를 포함하는 int 배열을 반환합니다. 이 개수(count)는 JDBC 드라이버에 의해 보고됩니다. 개수(count)를 사용할 수 없는 경우 JDBC 드라이버는 -2 값을 반환합니다.
[Note]
이러한 시나리오에서는 기본PreparedStatement의 값이 자동으로 설정되므로 각 값에 해당하는 JDBC 타입이 지정된 Java 타입에서 파생되어야 합니다. 이는 일반적으로 잘 작동하지만 문제가 발생할 가능성이 있습니다(예: 맵에 포함된null값). Spring은 이러한 경우 기본적으로ParameterMetaData.getParameterType을 호출하는데, 이는 JDBC 드라이버에 비용이 많이 들 수 있습니다. 최신 드라이버 버전을 사용해야 하며 성능 문제가 발생하는 경우(Oracle 12c, JBoss 및 PostgreSQL에서 보고된 대로)spring.jdbc.getParameterType.ignore속성(property)을true(JVM 시스템 속성으로 또는SpringProperties메커니즘을 통해)로 설정하는 것을 고려해야 합니다. ).또는
BatchPreparedStatementSetter(앞서 설명한 대로)를 통하거나,List<Object[]>기반 호출에 제공된 명시적 유형 배열을 통해, 또는 사용자 정의MapSqlParameterSource인스턴스에 대한registerSqlType호출을 통해, 또는 Java 선언 속성 타입에서 (null 값에 대해서도) SQL type을 파생시키는BeanPropertySqlParameterSource를 통해 해당 JDBC 유형을 명시적으로 지정하는 것을 고려할 수 있습니다.
배치 업데이트의 앞선 예는 너무 커서 여러 개의 작은 배치로 나누고 싶은 배치를 다룹니다. 이전에 언급한 방법으로 batchUpdate 메서드를 여러 번 호출하여 이 작업을 수행할 수 있지만 이제는 더 편리한 방법이 있습니다. 이 메서드는 SQL 문 외에도 매개 변수가 포함된 객체 Collection, 각 배치에 대해 수행할 업데이트 수 및 준비된 statement의 매개 변수 값을 설정하는 ParameterizedPreparedStatementSetter를 사용합니다. 프레임워크는 제공된 값을 반복(loop)하고 업데이트 호출을 지정된 크기의 배치로 나눕니다(break).
다음 예에서는 배치 크기 100을 사용하는 배치 업데이트를 보여줍니다.
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int[][] batchUpdate(final Collection<Actor> actors) {
int[][] updateCounts = jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
actors,
100,
(PreparedStatement ps, Actor actor) -> {
ps.setString(1, actor.getFirstName());
ps.setString(2, actor.getLastName());
ps.setLong(3, actor.getId().longValue());
});
return updateCounts;
}
// ... additional methods
}
이 호출에 대한 일괄 업데이트 메서드는 각 업데이트에 대해 영향을 받는 행 수의 배열과 함께 각 일괄 처리에 대한 배열 항목을 포함하는 int 배열의 배열을 반환합니다. 최상위 배열의 길이는 실행된 일괄 처리 수(batch's run)를 나타내고 두 번째 수준 배열의 길이는 해당 일괄 처리의 업데이트 수를 나타냅니다. 각 배치의 업데이트 수는 제공된 총 업데이트 객체 수에 따라 모든 배치에 대해 제공되는 배치 크기(더 적을 수 있는 마지막 배치 제외)여야 합니다. 각 업데이트 문의 업데이트 횟수는 JDBC 드라이버가 보고한 것입니다. 개수를 사용할 수 없는 경우 JDBC 드라이버는 -2 값을 반환합니다.