Skip to content

Commit 71330dd

Browse files
committed
Revise support for quoted identifiers in SimpleJdbcInsert
Closes gh-31208
1 parent a803206 commit 71330dd

File tree

5 files changed

+56
-27
lines changed

5 files changed

+56
-27
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ public String createInsertString(String... generatedKeyNames) {
312312
if (schemaName != null) {
313313
if (quoting) {
314314
insertStatement.append(identifierQuoteString);
315-
insertStatement.append(this.metaDataProvider.schemaNameToUse(schemaName));
315+
insertStatement.append(schemaName);
316316
insertStatement.append(identifierQuoteString);
317317
}
318318
else {
@@ -324,7 +324,7 @@ public String createInsertString(String... generatedKeyNames) {
324324
String tableName = getTableName();
325325
if (quoting) {
326326
insertStatement.append(identifierQuoteString);
327-
insertStatement.append(this.metaDataProvider.tableNameToUse(tableName));
327+
insertStatement.append(tableName);
328328
insertStatement.append(identifierQuoteString);
329329
}
330330
else {
@@ -341,7 +341,7 @@ public String createInsertString(String... generatedKeyNames) {
341341
}
342342
if (quoting) {
343343
insertStatement.append(identifierQuoteString);
344-
insertStatement.append(this.metaDataProvider.columnNameToUse(columnName));
344+
insertStatement.append(columnName);
345345
insertStatement.append(identifierQuoteString);
346346
}
347347
else {

spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ public final synchronized void compile() throws InvalidDataAccessApiUsageExcepti
275275
if (getTableName() == null) {
276276
throw new InvalidDataAccessApiUsageException("Table name is required");
277277
}
278+
if (isQuoteIdentifiers() && this.declaredColumns.isEmpty()) {
279+
throw new InvalidDataAccessApiUsageException(
280+
"Explicit column names must be provided when using quoted identifiers");
281+
}
278282
try {
279283
this.jdbcTemplate.afterPropertiesSet();
280284
}

spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,23 @@ public interface SimpleJdbcInsertOperations {
7373
* <p>If this method is invoked, the identifier quote string for the underlying
7474
* database will be used to quote SQL identifiers in generated SQL statements.
7575
* In this context, SQL identifiers refer to schema, table, and column names.
76+
* <p>When identifiers are quoted, explicit column names must be supplied via
77+
* {@link #usingColumns(String...)}. Furthermore, all identifiers for the
78+
* schema name, table name, and column names must match the corresponding
79+
* identifiers in the database's metadata regarding casing (mixed case,
80+
* uppercase, or lowercase).
7681
* @return this {@code SimpleJdbcInsert} (for method chaining)
7782
* @since 6.1
83+
* @see #withSchemaName(String)
84+
* @see #withTableName(String)
85+
* @see #usingColumns(String...)
7886
* @see java.sql.DatabaseMetaData#getIdentifierQuoteString()
87+
* @see java.sql.DatabaseMetaData#storesMixedCaseIdentifiers()
88+
* @see java.sql.DatabaseMetaData#storesMixedCaseQuotedIdentifiers()
89+
* @see java.sql.DatabaseMetaData#storesUpperCaseIdentifiers()
90+
* @see java.sql.DatabaseMetaData#storesUpperCaseQuotedIdentifiers()
91+
* @see java.sql.DatabaseMetaData#storesLowerCaseIdentifiers()
92+
* @see java.sql.DatabaseMetaData#storesLowerCaseQuotedIdentifiers()
7993
*/
8094
SimpleJdbcInsertOperations usingQuotedIdentifiers();
8195

spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertIntegrationTests.java

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,13 @@ void retrieveColumnNamesFromMetadata() throws Exception {
5656
insertJaneSmith(insert);
5757
}
5858

59-
@Test // gh-24013
60-
void retrieveColumnNamesFromMetadataAndUsingQuotedIdentifiers() throws Exception {
61-
SimpleJdbcInsert insert = new SimpleJdbcInsert(embeddedDatabase)
62-
.withTableName("users")
63-
.usingGeneratedKeyColumns("id")
64-
.usingQuotedIdentifiers();
65-
66-
insert.compile();
67-
// NOTE: quoted identifiers in H2/HSQL will be UPPERCASE!
68-
assertThat(insert.getInsertString()).isEqualTo("INSERT INTO \"USERS\" (\"FIRST_NAME\", \"LAST_NAME\") VALUES(?, ?)");
69-
70-
insertJaneSmith(insert);
71-
}
72-
7359
@Test
7460
void usingColumns() {
7561
SimpleJdbcInsert insert = new SimpleJdbcInsert(embeddedDatabase)
62+
.withoutTableColumnMetaDataAccess()
7663
.withTableName("users")
77-
.usingColumns("first_name", "last_name");
64+
.usingColumns("first_name", "last_name")
65+
.usingGeneratedKeyColumns("id");
7866

7967
insert.compile();
8068
assertThat(insert.getInsertString()).isEqualTo("INSERT INTO users (first_name, last_name) VALUES(?, ?)");
@@ -84,13 +72,16 @@ void usingColumns() {
8472

8573
@Test // gh-24013
8674
void usingColumnsAndQuotedIdentifiers() throws Exception {
75+
// NOTE: unquoted identifiers in H2/HSQL must be converted to UPPERCASE
76+
// since that's how they are stored in the DB metadata.
8777
SimpleJdbcInsert insert = new SimpleJdbcInsert(embeddedDatabase)
88-
.withTableName("users")
89-
.usingColumns("first_name", "last_name")
78+
.withoutTableColumnMetaDataAccess()
79+
.withTableName("USERS")
80+
.usingColumns("FIRST_NAME", "LAST_NAME")
81+
.usingGeneratedKeyColumns("id")
9082
.usingQuotedIdentifiers();
9183

9284
insert.compile();
93-
// NOTE: quoted identifiers in H2/HSQL will be UPPERCASE!
9485
assertThat(insert.getInsertString()).isEqualToIgnoringNewLines("""
9586
INSERT INTO "USERS" ("FIRST_NAME", "LAST_NAME") VALUES(?, ?)
9687
""");
@@ -116,9 +107,11 @@ class CustomSchemaTests extends AbstractSimpleJdbcInsertIntegrationTests {
116107
@Test
117108
void usingColumnsWithSchemaName() {
118109
SimpleJdbcInsert insert = new SimpleJdbcInsert(embeddedDatabase)
110+
.withoutTableColumnMetaDataAccess()
119111
.withSchemaName("my_schema")
120112
.withTableName("users")
121-
.usingColumns("first_name", "last_name");
113+
.usingColumns("first_name", "last_name")
114+
.usingGeneratedKeyColumns("id");
122115

123116
insert.compile();
124117
assertThat(insert.getInsertString()).isEqualTo("INSERT INTO my_schema.users (first_name, last_name) VALUES(?, ?)");
@@ -128,14 +121,17 @@ void usingColumnsWithSchemaName() {
128121

129122
@Test // gh-24013
130123
void usingColumnsAndQuotedIdentifiersWithSchemaName() throws Exception {
124+
// NOTE: unquoted identifiers in H2/HSQL must be converted to UPPERCASE
125+
// since that's how they are stored in the DB metadata.
131126
SimpleJdbcInsert insert = new SimpleJdbcInsert(embeddedDatabase)
132-
.withSchemaName("my_schema")
133-
.withTableName("users")
134-
.usingColumns("first_name", "last_name")
127+
.withoutTableColumnMetaDataAccess()
128+
.withSchemaName("MY_SCHEMA")
129+
.withTableName("USERS")
130+
.usingColumns("FIRST_NAME", "LAST_NAME")
131+
.usingGeneratedKeyColumns("id")
135132
.usingQuotedIdentifiers();
136133

137134
insert.compile();
138-
// NOTE: quoted identifiers in H2/HSQL will be UPPERCASE!
139135
assertThat(insert.getInsertString()).isEqualToIgnoringNewLines("""
140136
INSERT INTO "MY_SCHEMA"."USERS" ("FIRST_NAME", "LAST_NAME") VALUES(?, ?)
141137
""");
@@ -182,7 +178,8 @@ protected void assertNumUsers(long count) {
182178
}
183179

184180
protected void insertJaneSmith(SimpleJdbcInsert insert) {
185-
insert.execute(Map.of("first_name", "Jane", "last_name", "Smith"));
181+
Number id = insert.executeAndReturnKey(Map.of("first_name", "Jane", "last_name", "Smith"));
182+
assertThat(id.intValue()).isEqualTo(2);
186183
assertNumUsers(2);
187184
}
188185

spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,20 @@ void missingTableName() throws Exception {
7777
connection.close();
7878
}
7979

80+
@Test // gh-24013 and gh-31208
81+
void usingQuotedIdentifiersWithoutSupplyingColumnNames() throws Exception {
82+
SimpleJdbcInsert insert = new SimpleJdbcInsert(dataSource)
83+
.withTableName("my_table")
84+
.usingQuotedIdentifiers();
85+
86+
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
87+
.isThrownBy(insert::compile)
88+
.withMessage("Explicit column names must be provided when using quoted identifiers");
89+
90+
// Appease the @AfterEach checks.
91+
connection.close();
92+
}
93+
8094
/**
8195
* This method does not test any functionality but rather only that
8296
* configuration methods can be chained without compiler errors.

0 commit comments

Comments
 (0)