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 2856f54985..e41f625ec5 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 @@ -248,6 +248,10 @@ public abstract class AbstractJdbcCall { * @param parameter the {@link SqlParameter} to add */ public void addDeclaredParameter(SqlParameter parameter) { + if (isCompiled()) { + throw new IllegalStateException("SqlCall for " + (isFunction() ? "function" : "procedure") + + " is already compiled"); + } Assert.notNull(parameter, "The supplied parameter must not be null"); if (!StringUtils.hasText(parameter.getName())) { throw new InvalidDataAccessApiUsageException( @@ -265,6 +269,10 @@ public abstract class AbstractJdbcCall { * @param rowMapper the RowMapper implementation to use */ public void addDeclaredRowMapper(String parameterName, RowMapper rowMapper) { + if (isCompiled()) { + throw new IllegalStateException("SqlCall for " + (isFunction() ? "function" : "procedure") + + " is already compiled"); + } 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 9d4342a785..064928f1dc 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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,17 +25,20 @@ import java.sql.Types; import javax.sql.DataSource; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; 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; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; @@ -360,4 +363,37 @@ class SimpleJdbcCallTests { verifyStatement(adder, "{call ADD_INVOICE(@AMOUNT = ?, @CUSTID = ?)}"); } + @Test + void declareParametersCannotBeInvokedWhenCompiled() { + SimpleJdbcCall call = new SimpleJdbcCall(dataSource) + .withProcedureName("procedure_name") + .declareParameters(new SqlParameter("PARAM", Types.VARCHAR)); + call.compile(); + assertThatIllegalStateException() + .isThrownBy(() -> call.declareParameters(new SqlParameter("Ignored Param", Types.VARCHAR))) + .withMessage("SqlCall for procedure is already compiled"); + } + + @Test + void addDeclaredRowMapperCanBeConfigured() { + SimpleJdbcCall call = new SimpleJdbcCall(dataSource) + .withProcedureName("procedure_name") + .returningResultSet("result_set", (rs, i) -> new Object()); + + assertThat(call).extracting("declaredRowMappers") + .asInstanceOf(InstanceOfAssertFactories.map(String.class, RowMapper.class)) + .containsOnlyKeys("result_set"); + } + + @Test + void addDeclaredRowMapperWhenCompiled() { + SimpleJdbcCall call = new SimpleJdbcCall(dataSource) + .withProcedureName("procedure_name") + .returningResultSet("result_set", (rs, i) -> new Object()); + call.compile(); + assertThatIllegalStateException() + .isThrownBy(() -> call.returningResultSet("not added", (rs, i) -> new Object())) + .withMessage("SqlCall for procedure is already compiled"); + } + }