Browse Source

JdbcTemplate consistently exposes first value of equally named columns

Issue: SPR-16578
pull/1854/head
Juergen Hoeller 7 years ago
parent
commit
7bce7504c7
  1. 11
      spring-jdbc/src/main/java/org/springframework/jdbc/core/ColumnMapRowMapper.java
  2. 2
      spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java
  3. 42
      spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java

11
spring-jdbc/src/main/java/org/springframework/jdbc/core/ColumnMapRowMapper.java

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -52,13 +52,12 @@ public class ColumnMapRowMapper implements RowMapper<Map<String, Object>> {
public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException { public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
ResultSetMetaData rsmd = rs.getMetaData(); ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount(); int columnCount = rsmd.getColumnCount();
Map<String, Object> mapOfColValues = createColumnMap(columnCount); Map<String, Object> mapOfColumnValues = createColumnMap(columnCount);
for (int i = 1; i <= columnCount; i++) { for (int i = 1; i <= columnCount; i++) {
String key = getColumnKey(JdbcUtils.lookupColumnName(rsmd, i)); String column = JdbcUtils.lookupColumnName(rsmd, i);
Object obj = getColumnValue(rs, i); mapOfColumnValues.putIfAbsent(getColumnKey(column), getColumnValue(rs, i));
mapOfColValues.put(key, obj);
} }
return mapOfColValues; return mapOfColumnValues;
} }
/** /**

2
spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java

@ -253,7 +253,7 @@ public class TableMetaDataContext {
for (Map.Entry<String, ?> entry : inParameters.entrySet()) { for (Map.Entry<String, ?> entry : inParameters.entrySet()) {
if (column.equalsIgnoreCase(entry.getKey())) { if (column.equalsIgnoreCase(entry.getKey())) {
value = entry.getValue(); value = entry.getValue();
// TODO: break; break;
} }
} }
} }

42
spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java

@ -22,12 +22,14 @@ import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.SQLWarning; import java.sql.SQLWarning;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -704,10 +706,10 @@ public class JdbcTemplateTests {
@Test @Test
public void testBatchUpdateWithListOfObjectArrays() throws Exception { public void testBatchUpdateWithListOfObjectArrays() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; 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[] {100});
ids.add(new Object[] {200}); 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); given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true); mockDatabaseMetaData(true);
@ -730,11 +732,11 @@ public class JdbcTemplateTests {
@Test @Test
public void testBatchUpdateWithListOfObjectArraysPlusTypeInfo() throws Exception { public void testBatchUpdateWithListOfObjectArraysPlusTypeInfo() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; 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[] {100});
ids.add(new Object[] {200}); ids.add(new Object[] {200});
final int[] sqlTypes = new int[] {Types.NUMERIC}; 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); given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true); mockDatabaseMetaData(true);
@ -1070,14 +1072,13 @@ public class JdbcTemplateTests {
given(this.callableStatement.execute()).willReturn(true); given(this.callableStatement.execute()).willReturn(true);
given(this.callableStatement.getUpdateCount()).willReturn(-1); given(this.callableStatement.getUpdateCount()).willReturn(-1);
List<SqlParameter> params = new ArrayList<>(); SqlParameter param = new SqlReturnResultSet("", (RowCallbackHandler) rs -> {
params.add(new SqlReturnResultSet("", (RowCallbackHandler) rs -> {
throw new InvalidDataAccessApiUsageException(""); throw new InvalidDataAccessApiUsageException("");
})); });
this.thrown.expect(InvalidDataAccessApiUsageException.class); this.thrown.expect(InvalidDataAccessApiUsageException.class);
try { try {
this.template.call(conn -> conn.prepareCall("my query"), params); this.template.call(conn -> conn.prepareCall("my query"), Collections.singletonList(param));
} }
finally { finally {
verify(this.resultSet).close(); verify(this.resultSet).close();
@ -1099,10 +1100,8 @@ public class JdbcTemplateTests {
assertTrue("now it should have been set to case insensitive", assertTrue("now it should have been set to case insensitive",
this.template.isResultsMapCaseInsensitive()); this.template.isResultsMapCaseInsensitive());
List<SqlParameter> params = new ArrayList<>(); Map<String, Object> out = this.template.call(
params.add(new SqlOutParameter("a", 12)); conn -> conn.prepareCall("my query"), Collections.singletonList(new SqlOutParameter("a", 12)));
Map<String, Object> out = this.template.call(conn -> conn.prepareCall("my query"), params);
assertThat(out, instanceOf(LinkedCaseInsensitiveMap.class)); assertThat(out, instanceOf(LinkedCaseInsensitiveMap.class));
assertNotNull("we should have gotten the result with upper case", out.get("A")); assertNotNull("we should have gotten the result with upper case", out.get("A"));
@ -1111,6 +1110,25 @@ public class JdbcTemplateTests {
verify(this.connection).close(); verify(this.connection).close();
} }
@Test // SPR-16578
public void testEquallyNamedColumn() throws SQLException {
given(this.connection.createStatement()).willReturn(this.statement);
ResultSetMetaData metaData = mock(ResultSetMetaData.class);
given(metaData.getColumnCount()).willReturn(2);
given(metaData.getColumnLabel(1)).willReturn("x");
given(metaData.getColumnLabel(2)).willReturn("X");
given(this.resultSet.getMetaData()).willReturn(metaData);
given(this.resultSet.next()).willReturn(true, false);
given(this.resultSet.getObject(1)).willReturn("first value");
given(this.resultSet.getObject(2)).willReturn("second value");
Map<String, Object> map = this.template.queryForMap("my query");
assertEquals(1, map.size());
assertEquals("first value", map.get("x"));
}
private void mockDatabaseMetaData(boolean supportsBatchUpdates) throws SQLException { private void mockDatabaseMetaData(boolean supportsBatchUpdates) throws SQLException {
DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class); DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class);

Loading…
Cancel
Save