Prevent further configuration once SqlCall is compiled

This commit prevents AbstractJdbcCall to be further configured once it
is compiled.

See gh-33729
This commit is contained in:
Dan Sievewright 2024-10-17 02:12:18 -04:00 committed by Stéphane Nicoll
parent 83ab71725f
commit 8810913f30
2 changed files with 93 additions and 11 deletions

View File

@ -248,14 +248,16 @@ public abstract class AbstractJdbcCall {
* @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());
}
}
}
@ -265,9 +267,11 @@ public abstract class AbstractJdbcCall {
* @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);
}
}
}

View File

@ -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.junit.jupiter.api.Test;
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 @@ class SimpleJdbcCallTests {
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<SqlParameter> paramList = (List<SqlParameter>) 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<SqlParameter> paramList = (List<SqlParameter>) 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<String, RowMapper<?>> mappers = (Map<String, RowMapper<?>>) 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<String, RowMapper<?>> mappers = (Map<String, RowMapper<?>>) rowMappers.get(call);
assertThat(mappers).hasSize(1).allSatisfy((key,value) -> key.equals("result_set"));
}
}