diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java index b93ab0b6a92..ec40441a7aa 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java @@ -258,18 +258,29 @@ public abstract class ScriptUtils { for (String statement : statements) { stmtNumber++; try { - stmt.execute(statement); - int rowsAffected = stmt.getUpdateCount(); + boolean hasResultSet = stmt.execute(statement); + int updateCount = -1; if (logger.isDebugEnabled()) { - logger.debug(rowsAffected + " returned as update count for SQL: " + statement); - SQLWarning warningToLog = stmt.getWarnings(); - while (warningToLog != null) { - logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + - "', error code '" + warningToLog.getErrorCode() + - "', message [" + warningToLog.getMessage() + "]"); - warningToLog = warningToLog.getNextWarning(); - } + logSqlWarnings(stmt); } + do { + if (hasResultSet) { + // We invoke getResultSet() to ensure the JDBC driver processes + // it, but we intentionally ignore the returned ResultSet since + // we cannot do anything meaningful with it here. + stmt.getResultSet(); + if (logger.isDebugEnabled()) { + logger.debug("ResultSet returned for SQL: " + statement); + } + } + else { + updateCount = stmt.getUpdateCount(); + if (updateCount >= 0 && logger.isDebugEnabled()) { + logger.debug(updateCount + " returned as update count for SQL: " + statement); + } + } + hasResultSet = stmt.getMoreResults(); + } while (hasResultSet || updateCount != -1); } catch (SQLException ex) { boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop"); @@ -307,6 +318,16 @@ public abstract class ScriptUtils { } } + private static void logSqlWarnings(Statement stmt) throws SQLException { + SQLWarning warningToLog = stmt.getWarnings(); + while (warningToLog != null) { + logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + + "', error code '" + warningToLog.getErrorCode() + + "', message [" + warningToLog.getMessage() + "]"); + warningToLog = warningToLog.getNextWarning(); + } + } + /** * Read a script from the provided resource, using the supplied comment prefixes * and statement separator, and build a {@code String} containing the lines. diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientIntegrationTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientIntegrationTests.java index fd4daf2064d..55130eb1ec9 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientIntegrationTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientIntegrationTests.java @@ -74,7 +74,7 @@ class JdbcClientIntegrationTests { @Test void updateWithGeneratedKeys() { - int expectedId = 2; + int expectedId = 1; String firstName = "Jane"; String lastName = "Smith"; @@ -92,7 +92,7 @@ class JdbcClientIntegrationTests { @Test void updateWithGeneratedKeysAndKeyColumnNames() { - int expectedId = 2; + int expectedId = 1; String firstName = "Jane"; String lastName = "Smith"; @@ -110,7 +110,7 @@ class JdbcClientIntegrationTests { @Test void updateWithGeneratedKeysUsingNamedParameters() { - int expectedId = 2; + int expectedId = 1; String firstName = "Jane"; String lastName = "Smith"; @@ -129,7 +129,7 @@ class JdbcClientIntegrationTests { @Test void updateWithGeneratedKeysAndKeyColumnNamesUsingNamedParameters() { - int expectedId = 2; + int expectedId = 1; String firstName = "Jane"; String lastName = "Smith"; @@ -217,7 +217,7 @@ class JdbcClientIntegrationTests { private static void assertResults(List users) { - assertThat(users).containsExactly(new User(2, "John", "John"), new User(3, "John", "Smith")); + assertThat(users).containsExactly(new User(1, "John", "John"), new User(2, "John", "Smith")); } record Name(String name) {} diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertIntegrationTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertIntegrationTests.java index 02928652e53..720fbb330a4 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertIntegrationTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertIntegrationTests.java @@ -323,7 +323,7 @@ class SimpleJdbcInsertIntegrationTests { protected void insertJaneSmith(SimpleJdbcInsert insert) { Number id = insert.executeAndReturnKey(Map.of("first_name", "Jane", "last_name", "Smith")); - assertThat(id.intValue()).isEqualTo(2); + assertThat(id.intValue()).isEqualTo(1); assertNumRows(2); } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsIntegrationTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsIntegrationTests.java index 8eb91b29689..f59db0e6a46 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsIntegrationTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsIntegrationTests.java @@ -16,13 +16,24 @@ package org.springframework.jdbc.datasource.init; +import java.nio.charset.StandardCharsets; import java.sql.SQLException; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.Parameter; +import org.junit.jupiter.params.ParameterizedClass; +import org.junit.jupiter.params.provider.EnumSource; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.EncodedResource; +import org.springframework.jdbc.core.DataClassRowMapper; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; import static org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript; /** @@ -32,16 +43,22 @@ import static org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScr * @since 4.0.3 * @see ScriptUtilsTests */ +@ParameterizedClass +@EnumSource(EmbeddedDatabaseType.class) class ScriptUtilsIntegrationTests extends AbstractDatabaseInitializationTests { + @Parameter + EmbeddedDatabaseType databaseType; + + @Override protected EmbeddedDatabaseType getEmbeddedDatabaseType() { - return EmbeddedDatabaseType.HSQL; + return this.databaseType; } @BeforeEach void setUpSchema() throws SQLException { - executeSqlScript(db.getConnection(), usersSchema()); + executeSqlScript(db.getConnection(), encodedResource(usersSchema()), false, true, "--", null, "/*", "*/"); } @Test @@ -59,4 +76,52 @@ class ScriptUtilsIntegrationTests extends AbstractDatabaseInitializationTests { assertUsersDatabaseCreated("Hoeller", "Brannen"); } + @Test + @SuppressWarnings("unchecked") + void statementWithMultipleResultSets() throws SQLException { + // Derby does not support multiple statements/ResultSets within a single Statement. + assumeThat(this.databaseType).isNotSameAs(EmbeddedDatabaseType.DERBY); + + EncodedResource resource = encodedResource(resource("users-data.sql")); + executeSqlScript(db.getConnection(), resource, false, true, "--", null, "/*", "*/"); + + assertUsersInDatabase(user("Sam", "Brannen")); + + resource = encodedResource(inlineResource(""" + SELECT last_name FROM users WHERE id = 0; + UPDATE users SET first_name = 'Jane' WHERE id = 0; + UPDATE users SET last_name = 'Smith' WHERE id = 0; + SELECT last_name FROM users WHERE id = 0; + GO + """)); + + String separator = "GO\n"; + executeSqlScript(db.getConnection(), resource, false, true, "--", separator, "/*", "*/"); + + assertUsersInDatabase(user("Jane", "Smith")); + } + + private void assertUsersInDatabase(User... expectedUsers) { + List users = jdbcTemplate.query("SELECT * FROM users WHERE id = 0", + new DataClassRowMapper<>(User.class)); + assertThat(users).containsExactly(expectedUsers); + } + + + private static EncodedResource encodedResource(Resource resource) { + return new EncodedResource(resource); + } + + private static Resource inlineResource(String sql) { + byte[] bytes = sql.getBytes(StandardCharsets.UTF_8); + return new ByteArrayResource(bytes, "inline SQL"); + } + + private static User user(String firstName, String lastName) { + return new User(0, firstName, lastName); + } + + record User(int id, String firstName, String lastName) { + } + } diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema-with-custom-schema.sql b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema-with-custom-schema.sql index 6da1c297820..0959c7e6955 100644 --- a/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema-with-custom-schema.sql +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema-with-custom-schema.sql @@ -5,7 +5,7 @@ SET SCHEMA my_schema; DROP TABLE users IF EXISTS; CREATE TABLE users ( - id INTEGER GENERATED BY DEFAULT AS IDENTITY, + id INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL ); diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema.sql b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema.sql index 523c4a7c2b1..d9cb2918b1c 100644 --- a/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema.sql +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema.sql @@ -1,7 +1,7 @@ DROP TABLE users IF EXISTS; CREATE TABLE users ( - id INTEGER GENERATED BY DEFAULT AS IDENTITY, + id INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL );