From d39034754fc99862fc80eb8bd860004afd4b6d5a Mon Sep 17 00:00:00 2001 From: "Kita, Maksim" Date: Mon, 18 Nov 2019 10:20:15 +0300 Subject: [PATCH] Support quoted identifiers in SimpleJdbcInsert See gh-24013 --- .../GenericTableMetaDataProvider.java | 19 +++++++++++ .../core/metadata/TableMetaDataContext.java | 25 +++++++++++++- .../core/metadata/TableMetaDataProvider.java | 7 ++++ .../jdbc/core/simple/AbstractJdbcInsert.java | 14 ++++++++ .../jdbc/core/simple/SimpleJdbcInsert.java | 6 ++++ .../simple/SimpleJdbcInsertOperations.java | 7 ++++ .../core/simple/SimpleJdbcInsertTests.java | 33 +++++++++++++++++++ 7 files changed, 110 insertions(+), 1 deletion(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericTableMetaDataProvider.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericTableMetaDataProvider.java index a4f96c55fad..ead4359d278 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericTableMetaDataProvider.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericTableMetaDataProvider.java @@ -77,6 +77,8 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider { /** Collection of TableParameterMetaData objects. */ private final List tableParameterMetaData = new ArrayList<>(); + /** the string used to quote SQL identifiers. */ + private String identifierQuoteString = ""; /** * Constructor used to initialize with provided database meta-data. @@ -213,6 +215,15 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider { logger.warn("Error retrieving 'DatabaseMetaData.storesLowerCaseIdentifiers': " + ex.getMessage()); } } + + try { + this.identifierQuoteString = databaseMetaData.getIdentifierQuoteString(); + } + catch (SQLException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Error retrieving 'DatabaseMetaData.getIdentifierQuoteString': " + ex.getMessage()); + } + } } @Override @@ -293,6 +304,14 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider { return this.databaseVersion; } + /** + * Provide access to identifier quote string. + */ + @Override + public String getIdentifierQuoteString() { + return this.identifierQuoteString; + } + /** * Method supporting the meta-data processing for a table. */ diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java index 99407396e37..04b8f89a91a 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java @@ -79,6 +79,9 @@ public class TableMetaDataContext { // Are we using generated key columns private boolean generatedKeyColumnsUsed = false; + // Are we using escaping for SQL identifiers + private boolean usingEscaping = false; + /** * Set the name of the table for this context. @@ -276,13 +279,24 @@ public class TableMetaDataContext { for (String key : generatedKeyNames) { keys.add(key.toUpperCase()); } + String identifierQuoteString = ""; + if (this.metaDataProvider != null && this.usingEscaping) { + identifierQuoteString = this.metaDataProvider.getIdentifierQuoteString(); + } StringBuilder insertStatement = new StringBuilder(); insertStatement.append("INSERT INTO "); if (getSchemaName() != null) { + insertStatement.append(identifierQuoteString); insertStatement.append(getSchemaName()); insertStatement.append('.'); + insertStatement.append(getTableName()); + insertStatement.append(identifierQuoteString); + } + else { + insertStatement.append(identifierQuoteString); + insertStatement.append(getTableName()); + insertStatement.append(identifierQuoteString); } - insertStatement.append(getTableName()); insertStatement.append(" ("); int columnCount = 0; for (String columnName : getTableColumns()) { @@ -291,7 +305,9 @@ public class TableMetaDataContext { if (columnCount > 1) { insertStatement.append(", "); } + insertStatement.append(identifierQuoteString); insertStatement.append(columnName); + insertStatement.append(identifierQuoteString); } } insertStatement.append(") VALUES("); @@ -381,4 +397,11 @@ public class TableMetaDataContext { return obtainMetaDataProvider().isGeneratedKeysColumnNameArraySupported(); } + public boolean isUsingEscaping() { + return this.usingEscaping; + } + + public void setUsingEscaping(boolean usingEscaping) { + this.usingEscaping = usingEscaping; + } } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProvider.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProvider.java index 3d87c74bd24..d776de207b9 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProvider.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProvider.java @@ -139,4 +139,11 @@ public interface TableMetaDataProvider { */ List getTableParameterMetaData(); + /** + * Retrieves the string used to quote SQL identifiers. This method returns a space " " if identifier quoting is not supported. + * {@link DatabaseMetaData#getIdentifierQuoteString()} + * @return database identifier quote string. + */ + String getIdentifierQuoteString(); + } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java index f94fd094995..454a38ab657 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java @@ -236,6 +236,20 @@ public abstract class AbstractJdbcInsert { return this.insertTypes; } + /** + * Set using using escaping. + */ + public void setUsingEscaping(boolean usingEscaping) { + this.tableMetaDataContext.setUsingEscaping(usingEscaping); + } + + /** + * Get using escaping. + */ + public boolean isUsingEscaping() { + return this.tableMetaDataContext.isUsingEscaping(); + } + //------------------------------------------------------------------------- // Methods handling compilation issues diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java index d78773fe504..b940cc47edf 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java @@ -114,6 +114,12 @@ public class SimpleJdbcInsert extends AbstractJdbcInsert implements SimpleJdbcIn return this; } + @Override + public SimpleJdbcInsert usingEscaping(boolean usingEscaping) { + setUsingEscaping(usingEscaping); + return this; + } + @Override public int execute(Map args) { return doExecute(args); diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java index eb640971817..00364faf090 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java @@ -82,6 +82,13 @@ public interface SimpleJdbcInsertOperations { */ SimpleJdbcInsertOperations includeSynonymsForTableColumnMetaData(); + /** + * Specify should sql identifiers be quoted. + * @param usingEscaping should sql identifiers be quoted + * @return the instance of this SimpleJdbcInsert + */ + SimpleJdbcInsertOperations usingEscaping(boolean usingEscaping); + /** * Execute the insert using the values passed in. diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertTests.java index 4cc53fed137..12d1c70e6e5 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertTests.java @@ -138,4 +138,37 @@ class SimpleJdbcInsertTests { verify(tableResultSet).close(); } + @Test + public void testSimpleJdbcInsert() { + SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(dataSource).withTableName("T").usingColumns("F", "S"); + jdbcInsert.compile(); + String expected = "INSERT INTO T (F, S) VALUES(?, ?)"; + String actual = jdbcInsert.getInsertString(); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testSimpleJdbcInsertWithEscapingWithSchemaName() throws Exception { + SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(dataSource).withSchemaName("S").withTableName("T").usingColumns("F", "S").usingEscaping(true); + + given(databaseMetaData.getIdentifierQuoteString()).willReturn("`"); + + jdbcInsert.compile(); + String expected = "INSERT INTO `S.T` (`F`, `S`) VALUES(?, ?)"; + String actual = jdbcInsert.getInsertString(); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testSimpleJdbcInsertWithEscapingWithoutSchemaName() throws Exception { + SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(dataSource).withTableName("T").usingColumns("F", "S").usingEscaping(true); + + given(databaseMetaData.getIdentifierQuoteString()).willReturn("`"); + + jdbcInsert.compile(); + String expected = "INSERT INTO `T` (`F`, `S`) VALUES(?, ?)"; + String actual = jdbcInsert.getInsertString(); + assertThat(actual).isEqualTo(expected); + } + }