From f70d14e2c2ccc5215f2c0e3571064e2a2362a156 Mon Sep 17 00:00:00 2001 From: Thomas Risberg Date: Thu, 11 Jun 2009 20:54:09 +0000 Subject: [PATCH] added support to SimpleJdbcInsert for including synonyms when table metadata is retrieved; this only applies to Oracle (SPR-4782) --- .../GenericTableMetaDataProvider.java | 4 +- .../metadata/OracleTableMetaDataProvider.java | 104 ++++++++++++++++++ .../core/metadata/TableMetaDataContext.java | 32 ++++-- .../TableMetaDataProviderFactory.java | 14 ++- .../jdbc/core/simple/AbstractJdbcInsert.java | 14 +++ .../jdbc/core/simple/SimpleJdbcInsert.java | 10 ++ .../simple/SimpleJdbcInsertOperations.java | 15 +++ 7 files changed, 178 insertions(+), 15 deletions(-) create mode 100644 org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/OracleTableMetaDataProvider.java diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericTableMetaDataProvider.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericTableMetaDataProvider.java index a1f48d648bd..06dc0dc7a21 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericTableMetaDataProvider.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericTableMetaDataProvider.java @@ -361,7 +361,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider { } } catch (SQLException se) { - logger.warn("Error while retreiving metadata for procedure columns: " + se.getMessage()); + logger.warn("Error while retreiving metadata for table columns: " + se.getMessage()); } finally { try { @@ -369,7 +369,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider { tableColumns.close(); } catch (SQLException se) { - logger.warn("Problem closing resultset for procedure column metadata " + se.getMessage()); + logger.warn("Problem closing resultset for table column metadata " + se.getMessage()); } } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/OracleTableMetaDataProvider.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/OracleTableMetaDataProvider.java new file mode 100644 index 00000000000..e3dde1927a2 --- /dev/null +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/OracleTableMetaDataProvider.java @@ -0,0 +1,104 @@ +/* + * Copyright 2002-2007 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.jdbc.core.metadata; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.sql.Connection; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +import org.springframework.dao.InvalidDataAccessApiUsageException; + +/** + * The Oracle specific implementation of the {@link org.springframework.jdbc.core.metadata.TableMetaDataProvider}. + * Supports a feature for including synonyms in the metadata lookup. + * + * @author Thomas Risberg + * @since 3.0 + */ +public class OracleTableMetaDataProvider extends GenericTableMetaDataProvider { + + private boolean includeSynonyms; + + + public OracleTableMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException { + this(databaseMetaData, false); + } + + public OracleTableMetaDataProvider(DatabaseMetaData databaseMetaData, boolean includeSynonyms) throws SQLException { + super(databaseMetaData); + this.includeSynonyms = includeSynonyms; + } + + + @Override + public void initializeWithTableColumnMetaData(DatabaseMetaData databaseMetaData, + String catalogName, String schemaName, String tableName) throws SQLException { + + Connection con = databaseMetaData.getConnection(); + Method methodToInvoke = null; + Boolean origValueForIncludeSynonyms = null; + + if (includeSynonyms) { + if (con.getClass().getName().startsWith("oracle")) { + if (logger.isDebugEnabled()) { + logger.debug("Including synonyms in table metadata lookup."); + } + } + else { + logger.warn("Unable to include synonyms in table metadata lookup. Connection used for " + + "DatabaseMetaData is not recognized as an Oracle connection; " + + "class is " + con.getClass().getName()); + } + + } + else { + if (logger.isDebugEnabled()) { + logger.debug("Defaulting to no synonyms in table metadata lookup."); + } + } + + if (includeSynonyms && con.getClass().getName().startsWith("oracle")) { + try { + methodToInvoke = con.getClass().getMethod("getIncludeSynonyms", (Class[]) null); + methodToInvoke.setAccessible(true); + origValueForIncludeSynonyms = (Boolean)methodToInvoke.invoke(con); + methodToInvoke = con.getClass().getMethod("setIncludeSynonyms", new Class[] {boolean.class}); + methodToInvoke.setAccessible(true); + methodToInvoke.invoke(con, Boolean.TRUE); + } + catch (Exception ex) { + throw new InvalidDataAccessApiUsageException( + "Couldn't initialize Oracle Connection.", ex); + } + + } + super.initializeWithTableColumnMetaData(databaseMetaData, catalogName, schemaName, tableName); + if (includeSynonyms && con.getClass().getName().startsWith("oracle")) { + try { + methodToInvoke = con.getClass().getMethod("setIncludeSynonyms", new Class[] {boolean.class}); + methodToInvoke.setAccessible(true); + methodToInvoke.invoke(con, origValueForIncludeSynonyms); + } + catch (Exception ex) { + throw new InvalidDataAccessApiUsageException( + "Couldn't restore Oracle Connection.", ex); + } + } + } +} \ No newline at end of file diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java index feb81535255..10057f67604 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java @@ -30,8 +30,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.jdbc.core.SqlTypeValue; -import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils; import org.springframework.jdbc.support.JdbcUtils; @@ -61,15 +59,17 @@ public class TableMetaDataContext { private List tableColumns = new ArrayList(); /** should we access insert parameter meta data info or not */ - private boolean accessTableParameterMetaData = true; + private boolean accessTableColumnMetaData = true; - /** the provider of call meta data */ + /** should we override default for including synonyms for meta data lookups */ + private boolean overrideIncludeSynonymsDefault = false; + + /** the provider of table meta data */ private TableMetaDataProvider metaDataProvider; /** are we using generated key columns */ private boolean generatedKeyColumnsUsed = false; - /** * Set the name of the table for this context. */ @@ -115,18 +115,32 @@ public class TableMetaDataContext { /** * Specify whether we should access table column meta data. */ - public void setAccessTableParameterMetaData(boolean accessTableParameterMetaData) { - this.accessTableParameterMetaData = accessTableParameterMetaData; + public void setAccessTableColumnMetaData(boolean accessTableColumnMetaData) { + this.accessTableColumnMetaData = accessTableColumnMetaData; } /** * Are we accessing table meta data? */ - public boolean isAccessTableParameterMetaData() { - return this.accessTableParameterMetaData; + public boolean isAccessTableColumnMetaData() { + return this.accessTableColumnMetaData; } + /** + * Specify whether we should override default for accessing synonyms. + */ + public void setOverrideIncludeSynonymsDefault(boolean override) { + this.overrideIncludeSynonymsDefault = override; + } + + /** + * Are we overrding include synonyms default? + */ + public boolean isOverrideIncludeSynonymsDefault() { + return this.overrideIncludeSynonymsDefault; + } + /** * Get a List of the table column names. */ diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProviderFactory.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProviderFactory.java index 25bee2c8efd..4e3ade188f0 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProviderFactory.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProviderFactory.java @@ -54,10 +54,15 @@ public class TableMetaDataProviderFactory { public Object processMetaData(DatabaseMetaData databaseMetaData) throws SQLException, MetaDataAccessException { - String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName()); - boolean accessTableColumnMetaData = context.isAccessTableParameterMetaData(); + String databaseProductName = + JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName()); + boolean accessTableColumnMetaData = context.isAccessTableColumnMetaData(); TableMetaDataProvider provider; - if ("HSQL Database Engine".equals(databaseProductName)) { + if ("Oracle".equals(databaseProductName)) { + provider = new OracleTableMetaDataProvider(databaseMetaData, + context.isOverrideIncludeSynonymsDefault()); + } + else if ("HSQL Database Engine".equals(databaseProductName)) { provider = new HsqlTableMetaDataProvider(databaseMetaData); } else if ("PostgreSQL".equals(databaseProductName)) { @@ -74,7 +79,8 @@ public class TableMetaDataProviderFactory { } provider.initializeWithMetaData(databaseMetaData); if (accessTableColumnMetaData) { - provider.initializeWithTableColumnMetaData(databaseMetaData, context.getCatalogName(), context.getSchemaName(), context.getTableName()); + provider.initializeWithTableColumnMetaData(databaseMetaData, context.getCatalogName(), + context.getSchemaName(), context.getTableName()); } return provider; } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java index 4370a94b8fe..d5368823218 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java @@ -192,6 +192,20 @@ public abstract class AbstractJdbcInsert { this.generatedKeyNames = new String[] {generatedKeyName}; } + /** + * Specify whether the parameter metadata for the call should be used. The default is true. + */ + public void setAccessTableColumnMetaData(boolean accessTableColumnMetaData) { + this.tableMetaDataContext.setAccessTableColumnMetaData(accessTableColumnMetaData); + } + + /** + * Specify whether the default for including synonyms should be changed. The default is false. + */ + public void setOverrideIncludeSynonymsDefault(boolean override) { + this.tableMetaDataContext.setOverrideIncludeSynonymsDefault(override); + } + /** * Get the insert string to be used */ diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java index cd57b26d76b..6192f006e08 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java @@ -94,6 +94,16 @@ public class SimpleJdbcInsert extends AbstractJdbcInsert implements SimpleJdbcIn return this; } + public SimpleJdbcInsertOperations withoutTableColumnMetaDataAccess() { + setAccessTableColumnMetaData(false); + return this; + } + + public SimpleJdbcInsertOperations includeSynonymsForTableColumnMetaData() { + setOverrideIncludeSynonymsDefault(true); + return this; + } + public int execute(Map args) { return doExecute(args); } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java index 4a3dcc47bb8..b51a615ffbb 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java @@ -66,6 +66,21 @@ public interface SimpleJdbcInsertOperations { */ SimpleJdbcInsertOperations usingGeneratedKeyColumns(String... columnNames); + /** + * Turn off any processing of column meta data information obtained via JDBC. + * @return the instance of this SimpleJdbcInsert + */ + SimpleJdbcInsertOperations withoutTableColumnMetaDataAccess(); + + /** + * Include synonyms for the column meta data lookups via JDBC. + * Note: this is only necessary to include for Oracle since other + * databases supporting synonyms seems to include the synonyms + * automatically. + * @return the instance of this SimpleJdbcInsert + */ + SimpleJdbcInsertOperations includeSynonymsForTableColumnMetaData(); + /** * Execute the insert using the values passed in.