revised OracleTableMetaDataProvider for reliable Oracle Connection detection; autodetect JdbcTemplate's NativeJdbcExtractor (SPR-7611)

This commit is contained in:
Juergen Hoeller 2010-10-14 21:25:14 +00:00
parent 1f1577e33e
commit 0f924820e8
5 changed files with 254 additions and 243 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2007 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -33,8 +33,8 @@ import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor; import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
/** /**
* A generic implementation of the {@link TableMetaDataProvider} that should provide enough features for all supported * A generic implementation of the {@link TableMetaDataProvider} that should provide
* databases. * enough features for all supported databases.
* *
* @author Thomas Risberg * @author Thomas Risberg
* @since 2.5 * @since 2.5
@ -67,30 +67,23 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
/** database products we know not supporting the use of a String[] for generated keys */ /** database products we know not supporting the use of a String[] for generated keys */
private List productsNotSupportingGeneratedKeysColumnNameArray = private List productsNotSupportingGeneratedKeysColumnNameArray =
Arrays.asList(new String[] {"Apache Derby", "HSQL Database Engine"}); Arrays.asList("Apache Derby", "HSQL Database Engine");
/** Collection of TableParameterMetaData objects */ /** Collection of TableParameterMetaData objects */
private List<TableParameterMetaData> insertParameterMetaData = new ArrayList<TableParameterMetaData>(); private List<TableParameterMetaData> insertParameterMetaData = new ArrayList<TableParameterMetaData>();
/** NativeJdbcExtractor that can be used to retrieve the native connection */ /** NativeJdbcExtractor that can be used to retrieve the native connection */
protected NativeJdbcExtractor nativeJdbcExtractor = null; private NativeJdbcExtractor nativeJdbcExtractor;
/** /**
* Constructor used to initialize with provided database meta data. * Constructor used to initialize with provided database meta data.
* @param databaseMetaData meta data to be used * @param databaseMetaData meta data to be used
* @throws SQLException
*/ */
protected GenericTableMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException { protected GenericTableMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
userName = databaseMetaData.getUserName(); this.userName = databaseMetaData.getUserName();
} }
/**
* Get whether identifiers use upper case
*/
public boolean isStoresUpperCaseIdentifiers() {
return storesUpperCaseIdentifiers;
}
/** /**
* Specify whether identifiers use upper case * Specify whether identifiers use upper case
@ -100,29 +93,36 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
} }
/** /**
* Get whether identifiers use lower case * Get whether identifiers use upper case
*/ */
public boolean isStoresLowerCaseIdentifiers() { public boolean isStoresUpperCaseIdentifiers() {
return storesLowerCaseIdentifiers; return this.storesUpperCaseIdentifiers;
} }
/** /**
* Specify whether identifiers use lower case * Specify whether identifiers use lower case.
*/ */
public void setStoresLowerCaseIdentifiers(boolean storesLowerCaseIdentifiers) { public void setStoresLowerCaseIdentifiers(boolean storesLowerCaseIdentifiers) {
this.storesLowerCaseIdentifiers = storesLowerCaseIdentifiers; this.storesLowerCaseIdentifiers = storesLowerCaseIdentifiers;
} }
/**
* Get whether identifiers use lower case
*/
public boolean isStoresLowerCaseIdentifiers() {
return this.storesLowerCaseIdentifiers;
}
public boolean isTableColumnMetaDataUsed() { public boolean isTableColumnMetaDataUsed() {
return tableColumnMetaDataUsed; return this.tableColumnMetaDataUsed;
} }
public List<TableParameterMetaData> getTableParameterMetaData() { public List<TableParameterMetaData> getTableParameterMetaData() {
return insertParameterMetaData; return this.insertParameterMetaData;
} }
public boolean isGetGeneratedKeysSupported() { public boolean isGetGeneratedKeysSupported() {
return getGeneratedKeysSupported; return this.getGeneratedKeysSupported;
} }
public boolean isGetGeneratedKeysSimulated(){ public boolean isGetGeneratedKeysSimulated(){
@ -140,10 +140,6 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
this.getGeneratedKeysSupported = getGeneratedKeysSupported; this.getGeneratedKeysSupported = getGeneratedKeysSupported;
} }
public boolean isGeneratedKeysColumnNameArraySupported() {
return generatedKeysColumnNameArraySupported;
}
/** /**
* Specify whether a column name array is supported for generated keys * Specify whether a column name array is supported for generated keys
*/ */
@ -151,12 +147,20 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
this.generatedKeysColumnNameArraySupported = generatedKeysColumnNameArraySupported; this.generatedKeysColumnNameArraySupported = generatedKeysColumnNameArraySupported;
} }
public boolean isGeneratedKeysColumnNameArraySupported() {
return this.generatedKeysColumnNameArraySupported;
}
public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) { public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) {
this.nativeJdbcExtractor = nativeJdbcExtractor; this.nativeJdbcExtractor = nativeJdbcExtractor;
} }
public void initializeWithMetaData(DatabaseMetaData databaseMetaData) throws SQLException { protected NativeJdbcExtractor getNativeJdbcExtractor() {
return this.nativeJdbcExtractor;
}
public void initializeWithMetaData(DatabaseMetaData databaseMetaData) throws SQLException {
try { try {
if (databaseMetaData.supportsGetGeneratedKeys()) { if (databaseMetaData.supportsGetGeneratedKeys()) {
logger.debug("GetGeneratedKeys is supported"); logger.debug("GetGeneratedKeys is supported");
@ -172,7 +176,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
} }
try { try {
String databaseProductName = databaseMetaData.getDatabaseProductName(); String databaseProductName = databaseMetaData.getDatabaseProductName();
if (productsNotSupportingGeneratedKeysColumnNameArray.contains(databaseProductName)) { if (this.productsNotSupportingGeneratedKeysColumnNameArray.contains(databaseProductName)) {
logger.debug("GeneratedKeysColumnNameArray is not supported for " + databaseProductName); logger.debug("GeneratedKeysColumnNameArray is not supported for " + databaseProductName);
setGeneratedKeysColumnNameArraySupported(false); setGeneratedKeysColumnNameArraySupported(false);
} }
@ -185,7 +189,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
logger.warn("Error retrieving 'DatabaseMetaData.getDatabaseProductName' - " + se.getMessage()); logger.warn("Error retrieving 'DatabaseMetaData.getDatabaseProductName' - " + se.getMessage());
} }
try { try {
databaseVersion = databaseMetaData.getDatabaseProductVersion(); this.databaseVersion = databaseMetaData.getDatabaseProductVersion();
} }
catch (SQLException se) { catch (SQLException se) {
logger.warn("Error retrieving 'DatabaseMetaData.getDatabaseProductVersion' - " + se.getMessage()); logger.warn("Error retrieving 'DatabaseMetaData.getDatabaseProductVersion' - " + se.getMessage());
@ -205,47 +209,56 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
} }
public void initializeWithTableColumnMetaData(DatabaseMetaData databaseMetaData, String catalogName, String schemaName, String tableName) public void initializeWithTableColumnMetaData(DatabaseMetaData databaseMetaData, String catalogName,
throws SQLException { String schemaName, String tableName) throws SQLException {
tableColumnMetaDataUsed = true;
this.tableColumnMetaDataUsed = true;
locateTableAndProcessMetaData(databaseMetaData, catalogName, schemaName, tableName); locateTableAndProcessMetaData(databaseMetaData, catalogName, schemaName, tableName);
} }
public String tableNameToUse(String tableName) { public String tableNameToUse(String tableName) {
if (tableName == null) if (tableName == null) {
return null; return null;
else if (isStoresUpperCaseIdentifiers()) }
else if (isStoresUpperCaseIdentifiers()) {
return tableName.toUpperCase(); return tableName.toUpperCase();
else if(isStoresLowerCaseIdentifiers()) }
else if(isStoresLowerCaseIdentifiers()) {
return tableName.toLowerCase(); return tableName.toLowerCase();
else }
else {
return tableName; return tableName;
}
} }
public String catalogNameToUse(String catalogName) { public String catalogNameToUse(String catalogName) {
if (catalogName == null) if (catalogName == null) {
return null; return null;
else if (isStoresUpperCaseIdentifiers()) }
else if (isStoresUpperCaseIdentifiers()) {
return catalogName.toUpperCase(); return catalogName.toUpperCase();
else if(isStoresLowerCaseIdentifiers()) }
else if(isStoresLowerCaseIdentifiers()) {
return catalogName.toLowerCase(); return catalogName.toLowerCase();
else }
return catalogName; else {
return catalogName;
}
} }
public String schemaNameToUse(String schemaName) { public String schemaNameToUse(String schemaName) {
if (schemaName == null) if (schemaName == null) {
return null; return null;
else if (isStoresUpperCaseIdentifiers()) }
else if (isStoresUpperCaseIdentifiers()) {
return schemaName.toUpperCase(); return schemaName.toUpperCase();
else if(isStoresLowerCaseIdentifiers()) }
else if(isStoresLowerCaseIdentifiers()) {
return schemaName.toLowerCase(); return schemaName.toLowerCase();
else }
return schemaName; else {
return schemaName;
}
} }
public String metaDataCatalogNameToUse(String catalogName) { public String metaDataCatalogNameToUse(String catalogName) {
@ -261,19 +274,20 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
/** /**
* Provide access to version info for subclasses * Provide access to version info for subclasses.
*/ */
protected String getDatabaseVersion() { protected String getDatabaseVersion() {
return databaseVersion; return this.databaseVersion;
} }
/** /**
* Method supporting the metedata processing for a table * Method supporting the metedata processing for a table.
*/ */
private void locateTableAndProcessMetaData(DatabaseMetaData databaseMetaData, String catalogName, String schemaName, String tableName) { private void locateTableAndProcessMetaData(DatabaseMetaData databaseMetaData, String catalogName,
String schemaName, String tableName) {
Map<String, TableMetaData> tableMeta = new HashMap<String, TableMetaData>(); Map<String, TableMetaData> tableMeta = new HashMap<String, TableMetaData>();
ResultSet tables = null; ResultSet tables = null;
try { try {
tables = databaseMetaData.getTables( tables = databaseMetaData.getTables(
catalogNameToUse(catalogName), catalogNameToUse(catalogName),
@ -311,7 +325,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
logger.warn("Unable to locate table meta data for '" + tableName +"' -- column names must be provided"); logger.warn("Unable to locate table meta data for '" + tableName +"' -- column names must be provided");
} }
else { else {
TableMetaData tmd = null; TableMetaData tmd;
if (schemaName == null) { if (schemaName == null) {
tmd = tableMeta.get(userName.toUpperCase()); tmd = tableMeta.get(userName.toUpperCase());
if (tmd == null) { if (tmd == null) {
@ -320,14 +334,16 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
tmd = tableMeta.get("DBO"); tmd = tableMeta.get("DBO");
} }
if (tmd == null) { if (tmd == null) {
throw new DataAccessResourceFailureException("Unable to locate table meta data for '" + tableName + "' in the default schema"); throw new DataAccessResourceFailureException("Unable to locate table meta data for '" +
tableName + "' in the default schema");
} }
} }
} }
else { else {
tmd = tableMeta.get(schemaName.toUpperCase()); tmd = tableMeta.get(schemaName.toUpperCase());
if (tmd == null) { if (tmd == null) {
throw new DataAccessResourceFailureException("Unable to locate table meta data for '" + tableName + "' in the '" + schemaName + "' schema"); throw new DataAccessResourceFailureException("Unable to locate table meta data for '" +
tableName + "' in the '" + schemaName + "' schema");
} }
} }
@ -378,7 +394,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
dataType, dataType,
nullable nullable
); );
insertParameterMetaData.add(meta); this.insertParameterMetaData.add(meta);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Retrieved metadata: " logger.debug("Retrieved metadata: "
+ meta.getParameterName() + + meta.getParameterName() +
@ -389,7 +405,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
} }
} }
catch (SQLException se) { catch (SQLException se) {
logger.warn("Error while retreiving metadata for table columns: " + se.getMessage()); logger.warn("Error while retrieving metadata for table columns: " + se.getMessage());
} }
finally { finally {
try { try {
@ -397,7 +413,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
tableColumns.close(); tableColumns.close();
} }
catch (SQLException se) { catch (SQLException se) {
logger.warn("Problem closing resultset for table column metadata " + se.getMessage()); logger.warn("Problem closing ResultSet for table column metadata " + se.getMessage());
} }
} }
@ -405,45 +421,49 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
/** /**
* Class representing table meta data * Inner class representing table meta data.
*/ */
private class TableMetaData { private static class TableMetaData {
private String catalogName; private String catalogName;
private String schemaName; private String schemaName;
private String tableName; private String tableName;
private String type; private String type;
public String getCatalogName() {
return catalogName;
}
public void setCatalogName(String catalogName) { public void setCatalogName(String catalogName) {
this.catalogName = catalogName; this.catalogName = catalogName;
} }
public String getSchemaName() { public String getCatalogName() {
return schemaName; return this.catalogName;
} }
public void setSchemaName(String schemaName) { public void setSchemaName(String schemaName) {
this.schemaName = schemaName; this.schemaName = schemaName;
} }
public String getTableName() { public String getSchemaName() {
return tableName; return this.schemaName;
} }
public void setTableName(String tableName) { public void setTableName(String tableName) {
this.tableName = tableName; this.tableName = tableName;
} }
public String getType() { public String getTableName() {
return type; return this.tableName;
} }
public void setType(String type) { public void setType(String type) {
this.type = type; this.type = type;
} }
public String getType() {
return this.type;
}
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,17 +22,20 @@ import java.sql.DatabaseMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
import org.springframework.util.ReflectionUtils;
/** /**
* The Oracle specific implementation of the {@link org.springframework.jdbc.core.metadata.TableMetaDataProvider}. * Oracle-specific implementation of the {@link org.springframework.jdbc.core.metadata.TableMetaDataProvider}.
* Supports a feature for including synonyms in the metadata lookup. * Supports a feature for including synonyms in the metadata lookup.
* *
* @author Thomas Risberg * @author Thomas Risberg
* @author Juergen Hoeller
* @since 3.0 * @since 3.0
*/ */
public class OracleTableMetaDataProvider extends GenericTableMetaDataProvider { public class OracleTableMetaDataProvider extends GenericTableMetaDataProvider {
private boolean includeSynonyms; private final boolean includeSynonyms;
public OracleTableMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException { public OracleTableMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException {
@ -49,67 +52,61 @@ public class OracleTableMetaDataProvider extends GenericTableMetaDataProvider {
public void initializeWithTableColumnMetaData(DatabaseMetaData databaseMetaData, public void initializeWithTableColumnMetaData(DatabaseMetaData databaseMetaData,
String catalogName, String schemaName, String tableName) throws SQLException { String catalogName, String schemaName, String tableName) throws SQLException {
Connection con = null; if (!this.includeSynonyms) {
if (nativeJdbcExtractor == null) { logger.debug("Defaulting to no synonyms in table metadata lookup");
con = databaseMetaData.getConnection(); super.initializeWithTableColumnMetaData(databaseMetaData, catalogName, schemaName, tableName);
if (logger.isDebugEnabled()) { return;
logger.debug("Using meta data JDBC connection: " + con.getClass().getName());
}
}
else {
con = nativeJdbcExtractor.getNativeConnection(databaseMetaData.getConnection());
if (logger.isDebugEnabled()) {
logger.debug("Using native JDBC connection: " + con.getClass().getName());
}
}
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")) { Connection con = databaseMetaData.getConnection();
try { NativeJdbcExtractor nativeJdbcExtractor = getNativeJdbcExtractor();
methodToInvoke = con.getClass().getMethod("getIncludeSynonyms", (Class[]) null); if (nativeJdbcExtractor != null) {
methodToInvoke.setAccessible(true); con = nativeJdbcExtractor.getNativeConnection(con);
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);
}
} }
boolean isOracleCon;
try {
Class oracleConClass = getClass().getClassLoader().loadClass("oracle.jdbc.OracleConnection");
isOracleCon = oracleConClass.isInstance(con);
}
catch (ClassNotFoundException ex) {
if (logger.isInfoEnabled()) {
logger.info("Couldn't find Oracle JDBC API: " + ex);
}
isOracleCon = false;
}
if (!isOracleCon) {
logger.warn("Unable to include synonyms in table metadata lookup. Connection used for " +
"DatabaseMetaData is not recognized as an Oracle connection: " + con);
super.initializeWithTableColumnMetaData(databaseMetaData, catalogName, schemaName, tableName);
return;
}
logger.debug("Including synonyms in table metadata lookup");
Method setIncludeSynonyms;
Boolean originalValueForIncludeSynonyms;
try {
Method getIncludeSynonyms = con.getClass().getMethod("getIncludeSynonyms", (Class[]) null);
ReflectionUtils.makeAccessible(getIncludeSynonyms);
originalValueForIncludeSynonyms = (Boolean) getIncludeSynonyms.invoke(con);
setIncludeSynonyms = con.getClass().getMethod("setIncludeSynonyms", new Class[] {boolean.class});
ReflectionUtils.makeAccessible(setIncludeSynonyms);
setIncludeSynonyms.invoke(con, Boolean.TRUE);
}
catch (Exception ex) {
throw new InvalidDataAccessApiUsageException("Couldn't prepare Oracle Connection", ex);
}
super.initializeWithTableColumnMetaData(databaseMetaData, catalogName, schemaName, tableName); super.initializeWithTableColumnMetaData(databaseMetaData, catalogName, schemaName, tableName);
if (includeSynonyms && con.getClass().getName().startsWith("oracle")) {
try { try {
methodToInvoke = con.getClass().getMethod("setIncludeSynonyms", new Class[] {boolean.class}); setIncludeSynonyms.invoke(con, originalValueForIncludeSynonyms);
methodToInvoke.setAccessible(true); }
methodToInvoke.invoke(con, origValueForIncludeSynonyms); catch (Exception ex) {
} throw new InvalidDataAccessApiUsageException("Couldn't reset Oracle Connection", ex);
catch (Exception ex) {
throw new InvalidDataAccessApiUsageException(
"Couldn't restore Oracle Connection.", ex);
}
} }
} }
}
}

View File

@ -72,7 +72,8 @@ public class TableMetaDataContext {
private boolean generatedKeyColumnsUsed = false; private boolean generatedKeyColumnsUsed = false;
/** NativeJdbcExtractor to be used to retrieve the native connection */ /** NativeJdbcExtractor to be used to retrieve the native connection */
NativeJdbcExtractor nativeJdbcExtractor = null; NativeJdbcExtractor nativeJdbcExtractor;
/** /**
* Set the name of the table for this context. * Set the name of the table for this context.
@ -162,7 +163,7 @@ public class TableMetaDataContext {
/** /**
* Does this database support simple query to retrieve generated keys * Does this database support simple query to retrieve generated keys
* when the JDBC 3.0 feature is not supported * when the JDBC 3.0 feature is not supported.
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}? * {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}?
*/ */
public boolean isGetGeneratedKeysSimulated() { public boolean isGetGeneratedKeysSimulated() {
@ -171,7 +172,7 @@ public class TableMetaDataContext {
/** /**
* Does this database support simple query to retrieve generated keys * Does this database support simple query to retrieve generated keys
* when the JDBC 3.0 feature is not supported * when the JDBC 3.0 feature is not supported.
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}? * {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}?
*/ */
public String getSimulationQueryForGetGeneratedKey(String tableName, String keyColumnName) { public String getSimulationQueryForGetGeneratedKey(String tableName, String keyColumnName) {
@ -179,7 +180,7 @@ public class TableMetaDataContext {
} }
/** /**
* Is a column name String array for retrieving generated keys supported * Is a column name String array for retrieving generated keys supported?
* {@link java.sql.Connection#createStruct(String, Object[])}? * {@link java.sql.Connection#createStruct(String, Object[])}?
*/ */
public boolean isGeneratedKeysColumnNameArraySupported() { public boolean isGeneratedKeysColumnNameArraySupported() {
@ -187,7 +188,7 @@ public class TableMetaDataContext {
} }
/** /**
* Set {@link NativeJdbcExtractor} to be used to retrieve the native connection * Set {@link NativeJdbcExtractor} to be used to retrieve the native connection.
*/ */
public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) { public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) {
this.nativeJdbcExtractor = nativeJdbcExtractor; this.nativeJdbcExtractor = nativeJdbcExtractor;
@ -195,7 +196,7 @@ public class TableMetaDataContext {
/** /**
* Process the current meta data with the provided configuration options * Process the current meta data with the provided configuration options.
* @param dataSource the DataSource being used * @param dataSource the DataSource being used
* @param declaredColumns any columns that are declared * @param declaredColumns any columns that are declared
* @param generatedKeyNames name of generated keys * @param generatedKeyNames name of generated keys
@ -212,7 +213,7 @@ public class TableMetaDataContext {
*/ */
protected List<String> reconcileColumnsToUse(List<String> declaredColumns, String[] generatedKeyNames) { protected List<String> reconcileColumnsToUse(List<String> declaredColumns, String[] generatedKeyNames) {
if (generatedKeyNames.length > 0) { if (generatedKeyNames.length > 0) {
generatedKeyColumnsUsed = true; this.generatedKeyColumnsUsed = true;
} }
if (declaredColumns.size() > 0) { if (declaredColumns.size() > 0) {
return new ArrayList<String>(declaredColumns); return new ArrayList<String>(declaredColumns);
@ -240,7 +241,7 @@ public class TableMetaDataContext {
// database metadata is not necessarily providing case sensitive column names // database metadata is not necessarily providing case sensitive column names
Map caseInsensitiveParameterNames = Map caseInsensitiveParameterNames =
SqlParameterSourceUtils.extractCaseInsensitiveParameterNames(parameterSource); SqlParameterSourceUtils.extractCaseInsensitiveParameterNames(parameterSource);
for (String column : tableColumns) { for (String column : this.tableColumns) {
if (parameterSource.hasValue(column)) { if (parameterSource.hasValue(column)) {
values.add(SqlParameterSourceUtils.getTypedValue(parameterSource, column)); values.add(SqlParameterSourceUtils.getTypedValue(parameterSource, column));
} }
@ -280,7 +281,7 @@ public class TableMetaDataContext {
for (String key : inParameters.keySet()) { for (String key : inParameters.keySet()) {
source.put(key.toLowerCase(), inParameters.get(key)); source.put(key.toLowerCase(), inParameters.get(key));
} }
for (String column : tableColumns) { for (String column : this.tableColumns) {
values.add(source.get(column.toLowerCase())); values.add(source.get(column.toLowerCase()));
} }
return values; return values;
@ -316,7 +317,7 @@ public class TableMetaDataContext {
} }
insertStatement.append(") VALUES("); insertStatement.append(") VALUES(");
if (columnCount < 1) { if (columnCount < 1) {
if (generatedKeyColumnsUsed) { if (this.generatedKeyColumnsUsed) {
logger.info("Unable to locate non-key columns for table '" + logger.info("Unable to locate non-key columns for table '" +
this.getTableName() + "' so an empty insert statement is generated"); this.getTableName() + "' so an empty insert statement is generated");
} }
@ -340,15 +341,12 @@ public class TableMetaDataContext {
* @return the array of types to be used * @return the array of types to be used
*/ */
public int[] createInsertTypes() { public int[] createInsertTypes() {
int[] types = new int[this.getTableColumns().size()]; int[] types = new int[this.getTableColumns().size()];
List<TableParameterMetaData> parameters = this.metaDataProvider.getTableParameterMetaData(); List<TableParameterMetaData> parameters = this.metaDataProvider.getTableParameterMetaData();
Map<String, TableParameterMetaData> parameterMap = new HashMap<String, TableParameterMetaData>(parameters.size()); Map<String, TableParameterMetaData> parameterMap = new HashMap<String, TableParameterMetaData>(parameters.size());
for (TableParameterMetaData tpmd : parameters) { for (TableParameterMetaData tpmd : parameters) {
parameterMap.put(tpmd.getParameterName().toUpperCase(), tpmd); parameterMap.put(tpmd.getParameterName().toUpperCase(), tpmd);
} }
int typeIndx = 0; int typeIndx = 0;
for (String column : this.getTableColumns()) { for (String column : this.getTableColumns()) {
if (column == null) { if (column == null) {
@ -365,7 +363,6 @@ public class TableMetaDataContext {
} }
typeIndx++; typeIndx++;
} }
return types; return types;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2007 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +18,6 @@ package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -38,74 +37,70 @@ import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
*/ */
public class TableMetaDataProviderFactory { public class TableMetaDataProviderFactory {
/** Logger */
private static final Log logger = LogFactory.getLog(TableMetaDataProviderFactory.class); private static final Log logger = LogFactory.getLog(TableMetaDataProviderFactory.class);
/** /**
* Create a TableMetaDataProvider based on the database metedata * Create a TableMetaDataProvider based on the database metedata
* @param dataSource used to retrieve metedata * @param dataSource used to retrieve metedata
* @param context the class that holds configuration and metedata * @param context the class that holds configuration and metedata
* @return instance of the TableMetaDataProvider implementation to be used * @return instance of the TableMetaDataProvider implementation to be used
*/ */
static public TableMetaDataProvider createMetaDataProvider(DataSource dataSource, public static TableMetaDataProvider createMetaDataProvider(DataSource dataSource, TableMetaDataContext context) {
final TableMetaDataContext context) {
return createMetaDataProvider(dataSource, context, null); return createMetaDataProvider(dataSource, context, null);
} }
/** /**
* Create a TableMetaDataProvider based on the database metedata * Create a TableMetaDataProvider based on the database metedata
* @param dataSource used to retrieve metedata * @param dataSource used to retrieve metedata
* @param context the class that holds configuration and metedata * @param context the class that holds configuration and metedata
* @param nativeJdbcExtractor @{link NativeJdbcExtractor} to be used * @param nativeJdbcExtractor the NativeJdbcExtractor to be used
* @return instance of the TableMetaDataProvider implementation to be used * @return instance of the TableMetaDataProvider implementation to be used
*/ */
static public TableMetaDataProvider createMetaDataProvider(DataSource dataSource, public static TableMetaDataProvider createMetaDataProvider(DataSource dataSource,
final TableMetaDataContext context, final TableMetaDataContext context, final NativeJdbcExtractor nativeJdbcExtractor) {
final NativeJdbcExtractor nativeJdbcExtractor) {
try { try {
return (TableMetaDataProvider) JdbcUtils.extractDatabaseMetaData( return (TableMetaDataProvider) JdbcUtils.extractDatabaseMetaData(dataSource,
dataSource, new DatabaseMetaDataCallback() { new DatabaseMetaDataCallback() {
public Object processMetaData(DatabaseMetaData databaseMetaData) throws SQLException {
public Object processMetaData(DatabaseMetaData databaseMetaData) String databaseProductName =
throws SQLException, MetaDataAccessException { JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());
String databaseProductName = boolean accessTableColumnMetaData = context.isAccessTableColumnMetaData();
JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName()); TableMetaDataProvider provider;
boolean accessTableColumnMetaData = context.isAccessTableColumnMetaData(); if ("Oracle".equals(databaseProductName)) {
TableMetaDataProvider provider; provider = new OracleTableMetaDataProvider(databaseMetaData,
if ("Oracle".equals(databaseProductName)) { context.isOverrideIncludeSynonymsDefault());
provider = new OracleTableMetaDataProvider(databaseMetaData, }
context.isOverrideIncludeSynonymsDefault()); else if ("HSQL Database Engine".equals(databaseProductName)) {
} provider = new HsqlTableMetaDataProvider(databaseMetaData);
else if ("HSQL Database Engine".equals(databaseProductName)) { }
provider = new HsqlTableMetaDataProvider(databaseMetaData); else if ("PostgreSQL".equals(databaseProductName)) {
} provider = new PostgresTableMetaDataProvider(databaseMetaData);
else if ("PostgreSQL".equals(databaseProductName)) { }
provider = new PostgresTableMetaDataProvider(databaseMetaData); else if ("Apache Derby".equals(databaseProductName)) {
} provider = new DerbyTableMetaDataProvider(databaseMetaData);
else if ("Apache Derby".equals(databaseProductName)) { }
provider = new DerbyTableMetaDataProvider(databaseMetaData); else {
} provider = new GenericTableMetaDataProvider(databaseMetaData);
else { }
provider = new GenericTableMetaDataProvider(databaseMetaData); if (nativeJdbcExtractor != null) {
} provider.setNativeJdbcExtractor(nativeJdbcExtractor);
if (nativeJdbcExtractor != null) { }
provider.setNativeJdbcExtractor(nativeJdbcExtractor); if (logger.isDebugEnabled()) {
} logger.debug("Using " + provider.getClass().getSimpleName());
if (logger.isDebugEnabled()) { }
logger.debug("Using " + provider.getClass().getName()); provider.initializeWithMetaData(databaseMetaData);
} if (accessTableColumnMetaData) {
provider.initializeWithMetaData(databaseMetaData); provider.initializeWithTableColumnMetaData(databaseMetaData, context.getCatalogName(),
if (accessTableColumnMetaData) { context.getSchemaName(), context.getTableName());
provider.initializeWithTableColumnMetaData(databaseMetaData, context.getCatalogName(), }
context.getSchemaName(), context.getTableName()); return provider;
} }
return provider; });
} }
}); catch (MetaDataAccessException ex) {
} catch (MetaDataAccessException e) { throw new DataAccessResourceFailureException("Error retrieving database metadata", ex);
throw new DataAccessResourceFailureException("Error retreiving database metadata", e);
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2007 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,25 +18,24 @@ package org.springframework.jdbc.core.simple;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.ResultSet;
import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@ -57,6 +56,7 @@ import org.springframework.util.Assert;
* This class provides the base SPI for {@link SimpleJdbcInsert}. * This class provides the base SPI for {@link SimpleJdbcInsert}.
* *
* @author Thomas Risberg * @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.5 * @since 2.5
*/ */
public abstract class AbstractJdbcInsert { public abstract class AbstractJdbcInsert {
@ -65,10 +65,13 @@ public abstract class AbstractJdbcInsert {
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
/** Lower-level class used to execute SQL */ /** Lower-level class used to execute SQL */
private JdbcTemplate jdbcTemplate = new JdbcTemplate(); private final JdbcTemplate jdbcTemplate;
/** Context used to retrieve and manage database metadata */
private final TableMetaDataContext tableMetaDataContext = new TableMetaDataContext();
/** List of columns objects to be used in insert statement */ /** List of columns objects to be used in insert statement */
private List<String> declaredColumns = new ArrayList<String>(); private final List<String> declaredColumns = new ArrayList<String>();
/** /**
* Has this operation been compiled? Compilation means at * Has this operation been compiled? Compilation means at
@ -77,31 +80,30 @@ public abstract class AbstractJdbcInsert {
*/ */
private boolean compiled = false; private boolean compiled = false;
/** the generated string used for insert statement */ /** The generated string used for insert statement */
private String insertString; private String insertString;
/** the SQL Type information for the insert columns */ /** The SQL type information for the insert columns */
private int[] insertTypes; private int[] insertTypes;
/** the names of the columns holding the generated key */ /** The names of the columns holding the generated key */
private String[] generatedKeyNames = new String[] {}; private String[] generatedKeyNames = new String[0];
/** context used to retrieve and manage database metadata */
private TableMetaDataContext tableMetaDataContext = new TableMetaDataContext();
/** /**
* Constructor for sublasses to delegate to for setting the DataSource. * Constructor for sublasses to delegate to for setting the DataSource.
*/ */
protected AbstractJdbcInsert(DataSource dataSource) { protected AbstractJdbcInsert(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
} }
/** /**
* Constructor for sublasses to delegate to for setting the JdbcTemplate. * Constructor for sublasses to delegate to for setting the JdbcTemplate.
*/ */
protected AbstractJdbcInsert(JdbcTemplate jdbcTemplate) { protected AbstractJdbcInsert(JdbcTemplate jdbcTemplate) {
Assert.notNull(jdbcTemplate, "JdbcTemplate must not be null");
this.jdbcTemplate = jdbcTemplate; this.jdbcTemplate = jdbcTemplate;
setNativeJdbcExtractor(jdbcTemplate.getNativeJdbcExtractor());
} }
@ -109,26 +111,19 @@ public abstract class AbstractJdbcInsert {
// Methods dealing with configuaration properties // Methods dealing with configuaration properties
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
/**
* Get the name of the table for this insert
*/
public String getTableName() {
return tableMetaDataContext.getTableName();
}
/** /**
* Set the name of the table for this insert * Set the name of the table for this insert
*/ */
public void setTableName(String tableName) { public void setTableName(String tableName) {
checkIfConfigurationModificationIsAllowed(); checkIfConfigurationModificationIsAllowed();
tableMetaDataContext.setTableName(tableName); this.tableMetaDataContext.setTableName(tableName);
} }
/** /**
* Get the name of the schema for this insert * Get the name of the table for this insert
*/ */
public String getSchemaName() { public String getTableName() {
return tableMetaDataContext.getSchemaName(); return this.tableMetaDataContext.getTableName();
} }
/** /**
@ -136,14 +131,14 @@ public abstract class AbstractJdbcInsert {
*/ */
public void setSchemaName(String schemaName) { public void setSchemaName(String schemaName) {
checkIfConfigurationModificationIsAllowed(); checkIfConfigurationModificationIsAllowed();
tableMetaDataContext.setSchemaName(schemaName); this.tableMetaDataContext.setSchemaName(schemaName);
} }
/** /**
* Get the name of the catalog for this insert * Get the name of the schema for this insert
*/ */
public String getCatalogName() { public String getSchemaName() {
return tableMetaDataContext.getCatalogName(); return this.tableMetaDataContext.getSchemaName();
} }
/** /**
@ -151,7 +146,14 @@ public abstract class AbstractJdbcInsert {
*/ */
public void setCatalogName(String catalogName) { public void setCatalogName(String catalogName) {
checkIfConfigurationModificationIsAllowed(); checkIfConfigurationModificationIsAllowed();
tableMetaDataContext.setCatalogName(catalogName); this.tableMetaDataContext.setCatalogName(catalogName);
}
/**
* Get the name of the catalog for this insert
*/
public String getCatalogName() {
return this.tableMetaDataContext.getCatalogName();
} }
/** /**
@ -159,22 +161,22 @@ public abstract class AbstractJdbcInsert {
*/ */
public void setColumnNames(List<String> columnNames) { public void setColumnNames(List<String> columnNames) {
checkIfConfigurationModificationIsAllowed(); checkIfConfigurationModificationIsAllowed();
declaredColumns.clear(); this.declaredColumns.clear();
declaredColumns.addAll(columnNames); this.declaredColumns.addAll(columnNames);
} }
/** /**
* Get the names of the columns used * Get the names of the columns used
*/ */
public List<String> getColumnNames() { public List<String> getColumnNames() {
return Collections.unmodifiableList(declaredColumns); return Collections.unmodifiableList(this.declaredColumns);
} }
/** /**
* Get the names of any generated keys * Get the names of any generated keys
*/ */
public String[] getGeneratedKeyNames() { public String[] getGeneratedKeyNames() {
return generatedKeyNames; return this.generatedKeyNames;
} }
/** /**
@ -207,32 +209,32 @@ public abstract class AbstractJdbcInsert {
this.tableMetaDataContext.setOverrideIncludeSynonymsDefault(override); this.tableMetaDataContext.setOverrideIncludeSynonymsDefault(override);
} }
/**
* Set the {@link NativeJdbcExtractor} to use to retrieve the native connection if necessary
*/
public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) {
this.tableMetaDataContext.setNativeJdbcExtractor(nativeJdbcExtractor);
}
/** /**
* Get the insert string to be used * Get the insert string to be used
*/ */
public String getInsertString() { public String getInsertString() {
return insertString; return this.insertString;
} }
/** /**
* Get the array of {@link java.sql.Types} to be used for insert * Get the array of {@link java.sql.Types} to be used for insert
*/ */
public int[] getInsertTypes() { public int[] getInsertTypes() {
return insertTypes; return this.insertTypes;
} }
/** /**
* Get the {@link JdbcTemplate} that is configured to be used * Get the {@link JdbcTemplate} that is configured to be used
*/ */
protected JdbcTemplate getJdbcTemplate() { protected JdbcTemplate getJdbcTemplate() {
return jdbcTemplate; return this.jdbcTemplate;
}
/**
* Set the {@link NativeJdbcExtractor} to use to retrieve the native connection if necessary
*/
public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) {
this.tableMetaDataContext.setNativeJdbcExtractor(nativeJdbcExtractor);
} }