Consistently return empty array in case of empty batch arguments

Issue: SPR-17476

(cherry picked from commit 362c59c310)
This commit is contained in:
Juergen Hoeller
2018-11-08 13:43:07 +01:00
parent 8f1f36d6fb
commit 4f5ae22a52
4 changed files with 191 additions and 297 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,24 +25,29 @@ import java.util.List;
* Mainly for internal use within the framework.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 3.0
*/
public abstract class BatchUpdateUtils {
public static int[] executeBatchUpdate(
String sql, final List<Object[]> batchValues, final int[] columnTypes, JdbcOperations jdbcOperations) {
String sql, final List<Object[]> batchArgs, final int[] columnTypes, JdbcOperations jdbcOperations) {
if (batchArgs.isEmpty()) {
return new int[0];
}
return jdbcOperations.batchUpdate(
sql,
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Object[] values = batchValues.get(i);
Object[] values = batchArgs.get(i);
setStatementParameters(values, ps, columnTypes);
}
@Override
public int getBatchSize() {
return batchValues.size();
return batchArgs.size();
}
});
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,15 +28,16 @@ import org.springframework.jdbc.core.JdbcOperations;
* Mainly for internal use within the framework.
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 3.0
*/
public class NamedParameterBatchUpdateUtils extends BatchUpdateUtils {
public static int[] executeBatchUpdateWithNamedParameters(final ParsedSql parsedSql,
final SqlParameterSource[] batchArgs, JdbcOperations jdbcOperations) {
public static int[] executeBatchUpdateWithNamedParameters(
final ParsedSql parsedSql, final SqlParameterSource[] batchArgs, JdbcOperations jdbcOperations) {
if (batchArgs.length <= 0) {
return new int[] {0};
if (batchArgs.length == 0) {
return new int[0];
}
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, batchArgs[0]);

View File

@@ -28,6 +28,7 @@ import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -152,76 +153,46 @@ public class JdbcTemplateTests {
@Test
public void testStringsWithStaticSql() throws Exception {
doTestStrings(null, null, null, null, new JdbcTemplateCallback() {
@Override
public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
template.query(sql, rch);
}
});
doTestStrings(null, null, null, null, (template, sql, rch) -> template.query(sql, rch));
}
@Test
public void testStringsWithStaticSqlAndFetchSizeAndMaxRows() throws Exception {
doTestStrings(10, 20, 30, null, new JdbcTemplateCallback() {
@Override
public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
template.query(sql, rch);
}
});
doTestStrings(10, 20, 30, null, (template, sql, rch) -> template.query(sql, rch));
}
@Test
public void testStringsWithEmptyPreparedStatementSetter() throws Exception {
doTestStrings(null, null, null, null, new JdbcTemplateCallback() {
@Override
public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
template.query(sql, (PreparedStatementSetter) null, rch);
}
});
doTestStrings(null, null, null, null, (template, sql, rch) ->
template.query(sql, (PreparedStatementSetter) null, rch));
}
@Test
public void testStringsWithPreparedStatementSetter() throws Exception {
final Integer argument = 99;
doTestStrings(null, null, null, argument, new JdbcTemplateCallback() {
@Override
public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
template.query(sql, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setObject(1, argument);
}
}, rch);
}
});
doTestStrings(null, null, null, argument, (template, sql, rch) -> template.query(sql, ps -> {
ps.setObject(1, argument);
}, rch));
}
@Test
public void testStringsWithEmptyPreparedStatementArgs() throws Exception {
doTestStrings(null, null, null, null, new JdbcTemplateCallback() {
@Override
public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
template.query(sql, (Object[]) null, rch);
}
});
doTestStrings(null, null, null, null,
(template, sql, rch) -> template.query(sql, (Object[]) null, rch));
}
@Test
public void testStringsWithPreparedStatementArgs() throws Exception {
final Integer argument = 99;
doTestStrings(null, null, null, argument, new JdbcTemplateCallback() {
@Override
public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
template.query(sql, new Object[] { argument }, rch);
}
});
doTestStrings(null, null, null, argument,
(template, sql, rch) -> template.query(sql, new Object[] {argument}, rch));
}
private void doTestStrings(Integer fetchSize, Integer maxRows, Integer queryTimeout,
Object argument, JdbcTemplateCallback jdbcTemplateCallback) throws Exception {
String sql = "SELECT FORENAME FROM CUSTMR";
String[] results = { "rod", "gary", " portia" };
String[] results = {"rod", "gary", " portia"};
class StringHandler implements RowCallbackHandler {
private List<String> list = new LinkedList<>();
@@ -357,11 +328,8 @@ public class JdbcTemplateTests {
this.thrown.expect(sameInstance(runtimeException));
try {
this.template.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) {
throw runtimeException;
}
this.template.query(sql, (RowCallbackHandler) rs -> {
throw runtimeException;
});
}
finally {
@@ -398,10 +366,10 @@ public class JdbcTemplateTests {
given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected);
int actualRowsAffected = this.template.update(sql,
new Object[] {4, new SqlParameterValue(Types.NUMERIC, 2, new Float(1.4142))});
4, new SqlParameterValue(Types.NUMERIC, 2, Float.valueOf(1.4142f)));
assertTrue("Actual rows affected is correct", actualRowsAffected == rowsAffected);
verify(this.preparedStatement).setObject(1, 4);
verify(this.preparedStatement).setObject(2, new Float(1.4142), Types.NUMERIC, 2);
verify(this.preparedStatement).setObject(2, Float.valueOf(1.4142f), Types.NUMERIC, 2);
verify(this.preparedStatement).close();
verify(this.connection).close();
}
@@ -463,8 +431,7 @@ public class JdbcTemplateTests {
public void testBatchUpdateWithBatchFailure() throws Exception {
final String[] sql = {"A", "B", "C", "D"};
given(this.statement.executeBatch()).willThrow(
new BatchUpdateException(new int[] { 1, Statement.EXECUTE_FAILED, 1,
Statement.EXECUTE_FAILED }));
new BatchUpdateException(new int[] {1, Statement.EXECUTE_FAILED, 1, Statement.EXECUTE_FAILED}));
mockDatabaseMetaData(true);
given(this.connection.createStatement()).willReturn(this.statement);
@@ -525,17 +492,15 @@ public class JdbcTemplateTests {
@Test
public void testBatchUpdateWithPreparedStatement() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 };
final int[] rowsAffected = new int[] { 1, 2 };
final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
BatchPreparedStatementSetter setter =
new BatchPreparedStatementSetter() {
BatchPreparedStatementSetter setter = new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i)
throws SQLException {
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setInt(1, ids[i]);
}
@Override
@@ -561,8 +526,8 @@ public class JdbcTemplateTests {
@Test
public void testInterruptibleBatchUpdate() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 };
final int[] rowsAffected = new int[] { 1, 2 };
final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
@@ -602,8 +567,8 @@ public class JdbcTemplateTests {
@Test
public void testInterruptibleBatchUpdateWithBaseClass() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 };
final int[] rowsAffected = new int[] { 1, 2 };
final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
@@ -639,8 +604,8 @@ public class JdbcTemplateTests {
@Test
public void testInterruptibleBatchUpdateWithBaseClassAndNoBatchSupport() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 };
final int[] rowsAffected = new int[] { 1, 2 };
final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected[0], rowsAffected[1]);
mockDatabaseMetaData(false);
@@ -676,8 +641,8 @@ public class JdbcTemplateTests {
@Test
public void testBatchUpdateWithPreparedStatementAndNoBatchSupport() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 };
final int[] rowsAffected = new int[] { 1, 2 };
final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected[0], rowsAffected[1]);
@@ -707,7 +672,7 @@ public class JdbcTemplateTests {
@Test
public void testBatchUpdateFails() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 };
final int[] ids = new int[] {100, 200};
SQLException sqlException = new SQLException();
given(this.preparedStatement.executeBatch()).willThrow(sqlException);
@@ -738,21 +703,28 @@ public class JdbcTemplateTests {
}
}
@Test
public void testBatchUpdateWithEmptyList() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
int[] actualRowsAffected = template.batchUpdate(sql, Collections.emptyList());
assertTrue("executed 0 updates", actualRowsAffected.length == 0);
}
@Test
public void testBatchUpdateWithListOfObjectArrays() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final List<Object[]> ids = new ArrayList<>();
final List<Object[]> ids = new ArrayList<>(2);
ids.add(new Object[] {100});
ids.add(new Object[] {200});
final int[] rowsAffected = new int[] { 1, 2 };
final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
int[] actualRowsAffected = template.batchUpdate(sql, ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);
@@ -767,18 +739,17 @@ public class JdbcTemplateTests {
@Test
public void testBatchUpdateWithListOfObjectArraysPlusTypeInfo() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final List<Object[]> ids = new ArrayList<>();
final List<Object[]> ids = new ArrayList<>(2);
ids.add(new Object[] {100});
ids.add(new Object[] {200});
final int[] sqlTypes = new int[] {Types.NUMERIC};
final int[] rowsAffected = new int[] { 1, 2 };
final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
this.template = new JdbcTemplate(this.dataSource, false);
int[] actualRowsAffected = this.template.batchUpdate(sql, ids, sqlTypes);
int[] actualRowsAffected = this.template.batchUpdate(sql, ids, sqlTypes);
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);
@@ -793,23 +764,17 @@ public class JdbcTemplateTests {
public void testBatchUpdateWithCollectionOfObjects() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final List<Integer> ids = Arrays.asList(100, 200, 300);
final int[] rowsAffected1 = new int[] { 1, 2 };
final int[] rowsAffected2 = new int[] { 3 };
final int[] rowsAffected1 = new int[] {1, 2};
final int[] rowsAffected2 = new int[] {3};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected1, rowsAffected2);
mockDatabaseMetaData(true);
ParameterizedPreparedStatementSetter<Integer> setter = new ParameterizedPreparedStatementSetter<Integer>() {
@Override
public void setValues(PreparedStatement ps, Integer argument) throws SQLException {
ps.setInt(1, argument.intValue());
}
};
ParameterizedPreparedStatementSetter<Integer> setter = (ps, argument) -> ps.setInt(1, argument.intValue());
JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
int[][] actualRowsAffected = template.batchUpdate(sql, ids, 2, setter);
assertTrue("executed 2 updates", actualRowsAffected[0].length == 2);
assertEquals("executed 2 updates", 2, actualRowsAffected[0].length);
assertEquals(rowsAffected1[0], actualRowsAffected[0][0]);
assertEquals(rowsAffected1[1], actualRowsAffected[0][1]);
assertEquals(rowsAffected2[0], actualRowsAffected[1][0]);
@@ -823,19 +788,20 @@ public class JdbcTemplateTests {
}
@Test
public void testCouldntGetConnectionForOperationOrExceptionTranslator() throws SQLException {
public void testCouldNotGetConnectionForOperationOrExceptionTranslator() throws SQLException {
SQLException sqlException = new SQLException("foo", "07xxx");
this.dataSource = mock(DataSource.class);
given(this.dataSource.getConnection()).willThrow(sqlException);
JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
RowCountCallbackHandler rcch = new RowCountCallbackHandler();
this.thrown.expect(CannotGetJdbcConnectionException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch);
}
@Test
public void testCouldntGetConnectionForOperationWithLazyExceptionTranslator() throws SQLException {
public void testCouldNotGetConnectionForOperationWithLazyExceptionTranslator() throws SQLException {
SQLException sqlException = new SQLException("foo", "07xxx");
this.dataSource = mock(DataSource.class);
given(this.dataSource.getConnection()).willThrow(sqlException);
@@ -843,30 +809,31 @@ public class JdbcTemplateTests {
this.template.setDataSource(this.dataSource);
this.template.afterPropertiesSet();
RowCountCallbackHandler rcch = new RowCountCallbackHandler();
this.thrown.expect(CannotGetJdbcConnectionException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
this.template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch);
}
@Test
public void testCouldntGetConnectionInOperationWithExceptionTranslatorInitializedViaBeanProperty()
public void testCouldNotGetConnectionInOperationWithExceptionTranslatorInitializedViaBeanProperty()
throws SQLException {
doTestCouldntGetConnectionInOperationWithExceptionTranslatorInitialized(true);
doTestCouldNotGetConnectionInOperationWithExceptionTranslatorInitialized(true);
}
@Test
public void testCouldntGetConnectionInOperationWithExceptionTranslatorInitializedInAfterPropertiesSet()
public void testCouldNotGetConnectionInOperationWithExceptionTranslatorInitializedInAfterPropertiesSet()
throws SQLException {
doTestCouldntGetConnectionInOperationWithExceptionTranslatorInitialized(false);
doTestCouldNotGetConnectionInOperationWithExceptionTranslatorInitialized(false);
}
/**
* If beanProperty is true, initialize via exception translator bean property;
* if false, use afterPropertiesSet().
*/
private void doTestCouldntGetConnectionInOperationWithExceptionTranslatorInitialized(boolean beanProperty)
private void doTestCouldNotGetConnectionInOperationWithExceptionTranslatorInitialized(boolean beanProperty)
throws SQLException {
SQLException sqlException = new SQLException("foo", "07xxx");
@@ -897,14 +864,9 @@ public class JdbcTemplateTests {
given(this.preparedStatement.executeUpdate()).willReturn(expectedRowsUpdated);
PreparedStatementSetter pss = new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, name);
}
};
PreparedStatementSetter pss = ps -> ps.setString(1, name);
int actualRowsUpdated = new JdbcTemplate(this.dataSource).update(sql, pss);
assertTrue("updated correct # of rows", actualRowsUpdated == expectedRowsUpdated);
assertEquals("updated correct # of rows", actualRowsUpdated, expectedRowsUpdated);
verify(this.preparedStatement).setString(1, name);
verify(this.preparedStatement).close();
verify(this.connection).close();
@@ -917,12 +879,7 @@ public class JdbcTemplateTests {
SQLException sqlException = new SQLException();
given(this.preparedStatement.executeUpdate()).willThrow(sqlException);
PreparedStatementSetter pss = new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, name);
}
};
PreparedStatementSetter pss = ps -> ps.setString(1, name);
this.thrown.expect(DataAccessException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
try {
@@ -936,7 +893,7 @@ public class JdbcTemplateTests {
}
@Test
public void testCouldntClose() throws Exception {
public void testCouldNotClose() throws Exception {
SQLException sqlException = new SQLException("bar");
given(this.connection.createStatement()).willReturn(this.statement);
given(this.resultSet.next()).willReturn(false);
@@ -966,11 +923,8 @@ public class JdbcTemplateTests {
this.thrown.expect(SQLWarningException.class);
this.thrown.expect(exceptionCause(sameInstance(warnings)));
try {
t.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
rs.getByte(1);
}
t.query(sql, rs -> {
rs.getByte(1);
});
}
finally {
@@ -992,11 +946,8 @@ public class JdbcTemplateTests {
// Too long: truncation
this.template.setIgnoreWarnings(true);
this.template.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws java.sql.SQLException {
rs.getByte(1);
}
this.template.query(sql, rs -> {
rs.getByte(1);
});
verify(this.resultSet).close();
@@ -1016,11 +967,8 @@ public class JdbcTemplateTests {
this.thrown.expect(BadSqlGrammarException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
try {
this.template.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
throw sqlException;
}
this.template.query(sql, (RowCallbackHandler) rs -> {
throw sqlException;
});
fail("Should have thrown BadSqlGrammarException");
}
@@ -1047,11 +995,8 @@ public class JdbcTemplateTests {
this.thrown.expect(BadSqlGrammarException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
try {
template.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
throw sqlException;
}
template.query(sql, (RowCallbackHandler) rs -> {
throw sqlException;
});
}
finally {
@@ -1084,11 +1029,8 @@ public class JdbcTemplateTests {
this.thrown.expect(BadSqlGrammarException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
try {
template.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
throw sqlException;
}
template.query(sql, (RowCallbackHandler) rs -> {
throw sqlException;
});
}
finally {
@@ -1204,34 +1146,22 @@ public class JdbcTemplateTests {
given(this.connection.createStatement()).willReturn(this.statement);
try {
this.template.query("my query", new ResultSetExtractor<Object>() {
@Override
public Object extractData(ResultSet rs) {
throw new InvalidDataAccessApiUsageException("");
}
this.template.query("my query", (ResultSetExtractor<Object>) rs -> {
throw new InvalidDataAccessApiUsageException("");
});
fail("Should have thrown InvalidDataAccessApiUsageException");
}
catch (InvalidDataAccessApiUsageException idaauex) {
catch (InvalidDataAccessApiUsageException ex) {
// ok
}
try {
this.template.query(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con)
throws SQLException {
return con.prepareStatement("my query");
}
}, new ResultSetExtractor<Object>() {
@Override
public Object extractData(ResultSet rs2) {
throw new InvalidDataAccessApiUsageException("");
}
this.template.query(con -> con.prepareStatement("my query"), (ResultSetExtractor<Object>) rs2 -> {
throw new InvalidDataAccessApiUsageException("");
});
fail("Should have thrown InvalidDataAccessApiUsageException");
}
catch (InvalidDataAccessApiUsageException idaauex) {
catch (InvalidDataAccessApiUsageException ex) {
// ok
}
@@ -1247,24 +1177,13 @@ public class JdbcTemplateTests {
given(this.callableStatement.execute()).willReturn(true);
given(this.callableStatement.getUpdateCount()).willReturn(-1);
List<SqlParameter> params = new ArrayList<>();
params.add(new SqlReturnResultSet("", new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) {
throw new InvalidDataAccessApiUsageException("");
}
}));
SqlParameter param = new SqlReturnResultSet("", (RowCallbackHandler) rs -> {
throw new InvalidDataAccessApiUsageException("");
});
this.thrown.expect(InvalidDataAccessApiUsageException.class);
try {
this.template.call(new CallableStatementCreator() {
@Override
public CallableStatement createCallableStatement(Connection conn)
throws SQLException {
return conn.prepareCall("my query");
}
}, params);
this.template.call(conn -> conn.prepareCall("my query"), Collections.singletonList(param));
}
finally {
verify(this.resultSet).close();
@@ -1275,7 +1194,6 @@ public class JdbcTemplateTests {
@Test
public void testCaseInsensitiveResultsMap() throws Exception {
given(this.callableStatement.execute()).willReturn(false);
given(this.callableStatement.getUpdateCount()).willReturn(-1);
given(this.callableStatement.getObject(1)).willReturn("X");
@@ -1287,16 +1205,8 @@ public class JdbcTemplateTests {
assertTrue("now it should have been set to case insensitive",
this.template.isResultsMapCaseInsensitive());
List<SqlParameter> params = new ArrayList<>();
params.add(new SqlOutParameter("a", 12));
Map<String, Object> out = this.template.call(new CallableStatementCreator() {
@Override
public CallableStatement createCallableStatement(Connection conn)
throws SQLException {
return conn.prepareCall("my query");
}
}, params);
Map<String, Object> out = this.template.call(
conn -> conn.prepareCall("my query"), Collections.singletonList(new SqlOutParameter("a", 12)));
assertThat(out, instanceOf(LinkedCaseInsensitiveMap.class));
assertNotNull("we should have gotten the result with upper case", out.get("A"));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,14 +34,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.Customer;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameterValue;
import static org.junit.Assert.*;
@@ -55,32 +51,40 @@ import static org.mockito.BDDMockito.*;
public class NamedParameterJdbcTemplateTests {
private static final String SELECT_NAMED_PARAMETERS =
"select id, forename from custmr where id = :id and country = :country";
"select id, forename from custmr where id = :id and country = :country";
private static final String SELECT_NAMED_PARAMETERS_PARSED =
"select id, forename from custmr where id = ? and country = ?";
"select id, forename from custmr where id = ? and country = ?";
private static final String SELECT_NO_PARAMETERS =
"select id, forename from custmr";
private static final String UPDATE_NAMED_PARAMETERS =
"update seat_status set booking_id = null where performance_id = :perfId and price_band_id = :priceId";
"update seat_status set booking_id = null where performance_id = :perfId and price_band_id = :priceId";
private static final String UPDATE_NAMED_PARAMETERS_PARSED =
"update seat_status set booking_id = null where performance_id = ? and price_band_id = ?";
"update seat_status set booking_id = null where performance_id = ? and price_band_id = ?";
private static final String[] COLUMN_NAMES = new String[] {"id", "forename"};
@Rule
public ExpectedException thrown = ExpectedException.none();
private Connection connection;
private DataSource dataSource;
private PreparedStatement preparedStatement;
private ResultSet resultSet;
private DatabaseMetaData databaseMetaData;
private Map<String, Object> params = new HashMap<String, Object>();
private Map<String, Object> params = new HashMap<>();
private NamedParameterJdbcTemplate namedParameterTemplate;
@Before
public void setUp() throws Exception {
public void setup() throws Exception {
connection = mock(Connection.class);
dataSource = mock(DataSource.class);
preparedStatement = mock(PreparedStatement.class);
@@ -95,14 +99,15 @@ public class NamedParameterJdbcTemplateTests {
given(databaseMetaData.supportsBatchUpdates()).willReturn(true);
}
@Test
public void testNullDataSourceProvidedToCtor() throws Exception {
public void testNullDataSourceProvidedToCtor() {
thrown.expect(IllegalArgumentException.class);
new NamedParameterJdbcTemplate((DataSource) null);
}
@Test
public void testNullJdbcTemplateProvidedToCtor() throws Exception {
public void testNullJdbcTemplateProvidedToCtor() {
thrown.expect(IllegalArgumentException.class);
new NamedParameterJdbcTemplate((JdbcOperations) null);
}
@@ -114,14 +119,10 @@ public class NamedParameterJdbcTemplateTests {
params.put("perfId", 1);
params.put("priceId", 1);
Object result = namedParameterTemplate.execute(UPDATE_NAMED_PARAMETERS, params,
new PreparedStatementCallback<Object>() {
@Override
public Object doInPreparedStatement(PreparedStatement ps)
throws SQLException {
assertEquals(preparedStatement, ps);
ps.executeUpdate();
return "result";
}
(PreparedStatementCallback<Object>) ps -> {
assertEquals(preparedStatement, ps);
ps.executeUpdate();
return "result";
});
assertEquals("result", result);
@@ -139,14 +140,10 @@ public class NamedParameterJdbcTemplateTests {
params.put("perfId", new SqlParameterValue(Types.DECIMAL, 1));
params.put("priceId", new SqlParameterValue(Types.INTEGER, 1));
Object result = namedParameterTemplate.execute(UPDATE_NAMED_PARAMETERS, params,
new PreparedStatementCallback<Object>() {
@Override
public Object doInPreparedStatement(PreparedStatement ps)
throws SQLException {
assertEquals(preparedStatement, ps);
ps.executeUpdate();
return "result";
}
(PreparedStatementCallback<Object>) ps -> {
assertEquals(preparedStatement, ps);
ps.executeUpdate();
return "result";
});
assertEquals("result", result);
@@ -162,14 +159,10 @@ public class NamedParameterJdbcTemplateTests {
given(preparedStatement.executeUpdate()).willReturn(1);
Object result = namedParameterTemplate.execute(SELECT_NO_PARAMETERS,
new PreparedStatementCallback<Object>() {
@Override
public Object doInPreparedStatement(PreparedStatement ps)
throws SQLException {
assertEquals(preparedStatement, ps);
ps.executeQuery();
return "result";
}
(PreparedStatementCallback<Object>) ps -> {
assertEquals(preparedStatement, ps);
ps.executeQuery();
return "result";
});
assertEquals("result", result);
@@ -187,16 +180,12 @@ public class NamedParameterJdbcTemplateTests {
params.put("id", new SqlParameterValue(Types.DECIMAL, 1));
params.put("country", "UK");
Customer cust = namedParameterTemplate.query(SELECT_NAMED_PARAMETERS, params,
new ResultSetExtractor<Customer>() {
@Override
public Customer extractData(ResultSet rs) throws SQLException,
DataAccessException {
rs.next();
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
return cust;
}
rs -> {
rs.next();
Customer cust1 = new Customer();
cust1.setId(rs.getInt(COLUMN_NAMES[0]));
cust1.setForename(rs.getString(COLUMN_NAMES[1]));
return cust1;
});
assertTrue("Customer id was assigned correctly", cust.getId() == 1);
@@ -215,16 +204,12 @@ public class NamedParameterJdbcTemplateTests {
given(resultSet.getString("forename")).willReturn("rod");
Customer cust = namedParameterTemplate.query(SELECT_NO_PARAMETERS,
new ResultSetExtractor<Customer>() {
@Override
public Customer extractData(ResultSet rs) throws SQLException,
DataAccessException {
rs.next();
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
return cust;
}
rs -> {
rs.next();
Customer cust1 = new Customer();
cust1.setId(rs.getInt(COLUMN_NAMES[0]));
cust1.setForename(rs.getString(COLUMN_NAMES[1]));
return cust1;
});
assertTrue("Customer id was assigned correctly", cust.getId() == 1);
@@ -242,15 +227,12 @@ public class NamedParameterJdbcTemplateTests {
params.put("id", new SqlParameterValue(Types.DECIMAL, 1));
params.put("country", "UK");
final List<Customer> customers = new LinkedList<Customer>();
namedParameterTemplate.query(SELECT_NAMED_PARAMETERS, params, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
customers.add(cust);
}
final List<Customer> customers = new LinkedList<>();
namedParameterTemplate.query(SELECT_NAMED_PARAMETERS, params, rs -> {
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
customers.add(cust);
});
assertEquals(1, customers.size());
@@ -269,15 +251,12 @@ public class NamedParameterJdbcTemplateTests {
given(resultSet.getInt("id")).willReturn(1);
given(resultSet.getString("forename")).willReturn("rod");
final List<Customer> customers = new LinkedList<Customer>();
namedParameterTemplate.query(SELECT_NO_PARAMETERS, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
customers.add(cust);
}
final List<Customer> customers = new LinkedList<>();
namedParameterTemplate.query(SELECT_NO_PARAMETERS, rs -> {
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
customers.add(cust);
});
assertEquals(1, customers.size());
@@ -297,14 +276,11 @@ public class NamedParameterJdbcTemplateTests {
params.put("id", new SqlParameterValue(Types.DECIMAL, 1));
params.put("country", "UK");
List<Customer> customers = namedParameterTemplate.query(SELECT_NAMED_PARAMETERS, params,
new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int rownum) throws SQLException {
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
return cust;
}
(rs, rownum) -> {
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
return cust;
});
assertEquals(1, customers.size());
assertTrue("Customer id was assigned correctly", customers.get(0).getId() == 1);
@@ -323,14 +299,11 @@ public class NamedParameterJdbcTemplateTests {
given(resultSet.getString("forename")).willReturn("rod");
List<Customer> customers = namedParameterTemplate.query(SELECT_NO_PARAMETERS,
new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int rownum) throws SQLException {
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
return cust;
}
(rs, rownum) -> {
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
return cust;
});
assertEquals(1, customers.size());
assertTrue("Customer id was assigned correctly", customers.get(0).getId() == 1);
@@ -349,14 +322,11 @@ public class NamedParameterJdbcTemplateTests {
params.put("id", new SqlParameterValue(Types.DECIMAL, 1));
params.put("country", "UK");
Customer cust = namedParameterTemplate.queryForObject(SELECT_NAMED_PARAMETERS, params,
new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int rownum) throws SQLException {
Customer cust = new Customer();
cust.setId(rs.getInt(COLUMN_NAMES[0]));
cust.setForename(rs.getString(COLUMN_NAMES[1]));
return cust;
}
(rs, rownum) -> {
Customer cust1 = new Customer();
cust1.setId(rs.getInt(COLUMN_NAMES[0]));
cust1.setForename(rs.getString(COLUMN_NAMES[1]));
return cust1;
});
assertTrue("Customer id was assigned correctly", cust.getId() == 1);
assertTrue("Customer forename was assigned correctly", cust.getForename().equals("rod"));
@@ -405,15 +375,14 @@ public class NamedParameterJdbcTemplateTests {
final Map<String, Integer>[] ids = new Map[2];
ids[0] = Collections.singletonMap("id", 100);
ids[1] = Collections.singletonMap("id", 200);
final int[] rowsAffected = new int[] { 1, 2 };
final int[] rowsAffected = new int[] {1, 2};
given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData);
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
JdbcTemplate template = new JdbcTemplate(dataSource, false);
namedParameterTemplate = new NamedParameterJdbcTemplate(template);
int[] actualRowsAffected = namedParameterTemplate.batchUpdate("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);
@@ -425,20 +394,30 @@ public class NamedParameterJdbcTemplateTests {
verify(connection, atLeastOnce()).close();
}
@Test
public void testBatchUpdateWithEmptyMap() throws Exception {
@SuppressWarnings("unchecked")
final Map<String, Integer>[] ids = new Map[0];
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 0 updates", actualRowsAffected.length == 0);
}
@Test
public void testBatchUpdateWithSqlParameterSource() throws Exception {
SqlParameterSource[] ids = new SqlParameterSource[2];
ids[0] = new MapSqlParameterSource("id", 100);
ids[1] = new MapSqlParameterSource("id", 200);
final int[] rowsAffected = new int[] { 1, 2 };
final int[] rowsAffected = new int[] {1, 2};
given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData);
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
JdbcTemplate template = new JdbcTemplate(dataSource, false);
namedParameterTemplate = new NamedParameterJdbcTemplate(template);
int[] actualRowsAffected = namedParameterTemplate.batchUpdate("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);
@@ -455,15 +434,14 @@ public class NamedParameterJdbcTemplateTests {
SqlParameterSource[] ids = new SqlParameterSource[2];
ids[0] = new MapSqlParameterSource().addValue("id", 100, Types.NUMERIC);
ids[1] = new MapSqlParameterSource().addValue("id", 200, Types.NUMERIC);
final int[] rowsAffected = new int[] { 1, 2 };
final int[] rowsAffected = new int[] {1, 2};
given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData);
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
JdbcTemplate template = new JdbcTemplate(dataSource, false);
namedParameterTemplate = new NamedParameterJdbcTemplate(template);
int[] actualRowsAffected = namedParameterTemplate.batchUpdate("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);