diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java index d09c7e9e6c77..7c535100ec9a 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java @@ -249,14 +249,16 @@ protected CallableStatementCreatorFactory getCallableStatementFactory() { * @param parameter the {@link SqlParameter} to add */ public void addDeclaredParameter(SqlParameter parameter) { - Assert.notNull(parameter, "The supplied parameter must not be null"); - if (!StringUtils.hasText(parameter.getName())) { - throw new InvalidDataAccessApiUsageException( - "You must specify a parameter name when declaring parameters for \"" + getProcedureName() + "\""); - } - this.declaredParameters.add(parameter); - if (logger.isDebugEnabled()) { - logger.debug("Added declared parameter for [" + getProcedureName() + "]: " + parameter.getName()); + if(!isCompiled()) { + Assert.notNull(parameter, "The supplied parameter must not be null"); + if (!StringUtils.hasText(parameter.getName())) { + throw new InvalidDataAccessApiUsageException( + "You must specify a parameter name when declaring parameters for \"" + getProcedureName() + "\""); + } + this.declaredParameters.add(parameter); + if (logger.isDebugEnabled()) { + logger.debug("Added declared parameter for [" + getProcedureName() + "]: " + parameter.getName()); + } } } @@ -266,9 +268,11 @@ public void addDeclaredParameter(SqlParameter parameter) { * @param rowMapper the RowMapper implementation to use */ public void addDeclaredRowMapper(String parameterName, RowMapper rowMapper) { - this.declaredRowMappers.put(parameterName, rowMapper); - if (logger.isDebugEnabled()) { - logger.debug("Added row mapper for [" + getProcedureName() + "]: " + parameterName); + if(!isCompiled()) { + this.declaredRowMappers.put(parameterName, rowMapper); + if (logger.isDebugEnabled()) { + logger.debug("Added row mapper for [" + getProcedureName() + "]: " + parameterName); + } } } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java index 9d4342a78575..5075d50b912d 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcCallTests.java @@ -16,12 +16,15 @@ package org.springframework.jdbc.core.simple; +import java.lang.reflect.Field; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.util.List; +import java.util.Map; import javax.sql.DataSource; @@ -30,6 +33,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.jdbc.BadSqlGrammarException; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; @@ -360,4 +364,78 @@ void correctSybaseFunctionStatementNamed() throws Exception { verifyStatement(adder, "{call ADD_INVOICE(@AMOUNT = ?, @CUSTID = ?)}"); } + /** + * This test verifies that when declaring a parameter for a SimpleJdbcCall, + * then the parameter is added as expected. + */ + @SuppressWarnings("unchecked") + @Test + void verifyUncompiledDeclareParameterIsAdded() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + SimpleJdbcCall call = new SimpleJdbcCall(dataSource) + .withProcedureName("procedure_name") + .declareParameters(new SqlParameter("PARAM", Types.VARCHAR)); + + Field params = AbstractJdbcCall.class.getDeclaredField("declaredParameters"); + params.setAccessible(true); + List paramList = (List) params.get(call); + assertThat(paramList).hasSize(1).allMatch(sqlParam -> sqlParam.getName().equals("PARAM")); + } + + /** + * This verifies that once the SimpleJdbcCall is compiled, then adding + * a parameter is ignored + */ + @SuppressWarnings("unchecked") + @Test + void verifyWhenCompiledThenDeclareParameterIsIgnored() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + SimpleJdbcCall call = new SimpleJdbcCall(dataSource) + .withProcedureName("procedure_name") + .declareParameters(new SqlParameter("PARAM", Types.VARCHAR)); + call.compile(); + + call.declareParameters(new SqlParameter("Ignored Param", Types.VARCHAR)); + + Field params = AbstractJdbcCall.class.getDeclaredField("declaredParameters"); + params.setAccessible(true); + List paramList = (List) params.get(call); + assertThat(paramList).hasSize(1).allMatch(sqlParam -> sqlParam.getName().equals("PARAM")); + } + + /** + * When adding a declared row mapper, this verifies that the declaredRowMappers + * gets the new mapper + */ + @SuppressWarnings("unchecked") + @Test + void verifyUncompiledDeclareRowMapperIsAdded() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + SimpleJdbcCall call = new SimpleJdbcCall(dataSource) + .withProcedureName("procedure_name") + .returningResultSet("result_set", (rs,i) -> new Object()); + + Field rowMappers = AbstractJdbcCall.class.getDeclaredField("declaredRowMappers"); + rowMappers.setAccessible(true); + Map> mappers = (Map>) rowMappers.get(call); + assertThat(mappers).hasSize(1).allSatisfy((key,value) -> key.equals("result_set")); + } + + /** + * This verifies that when adding a row mapper after the call is compiled + * then the request is ignored + */ + @SuppressWarnings("unchecked") + @Test + void verifyWhenCompiledThenDeclareRowMapperIsIgnored() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + SimpleJdbcCall call = new SimpleJdbcCall(dataSource) + .withProcedureName("procedure_name") + .returningResultSet("result_set", (rs,i) -> new Object()); + call.compile(); + + call.returningResultSet("not added", (rs,i) -> new Object()); + + Field rowMappers = AbstractJdbcCall.class.getDeclaredField("declaredRowMappers"); + rowMappers.setAccessible(true); + Map> mappers = (Map>) rowMappers.get(call); + assertThat(mappers).hasSize(1).allSatisfy((key,value) -> key.equals("result_set")); + } + }