Skip to content

Commit

Permalink
Merge branch '5.2.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
jhoeller committed Sep 3, 2020
2 parents d550d34 + 670b9fd commit 3aae0fc
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 72 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
Expand All @@ -17,6 +17,7 @@
package org.springframework.scheduling.quartz;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;

import javax.sql.DataSource;
Expand Down Expand Up @@ -147,7 +148,8 @@ public void initialize() {

// No, if HSQL is the platform, we really don't want to use locks...
try {
String productName = JdbcUtils.extractDatabaseMetaData(this.dataSource, "getDatabaseProductName");
String productName = JdbcUtils.extractDatabaseMetaData(this.dataSource,
DatabaseMetaData::getDatabaseProductName);
productName = JdbcUtils.commonDatabaseName(productName);
if (productName != null && productName.toLowerCase().contains("hsql")) {
setUseDBLocks(false);
Expand Down
Expand Up @@ -73,7 +73,7 @@ private CallMetaDataProviderFactory() {
*/
public static CallMetaDataProvider createMetaDataProvider(DataSource dataSource, final CallMetaDataContext context) {
try {
return (CallMetaDataProvider) JdbcUtils.extractDatabaseMetaData(dataSource, databaseMetaData -> {
return JdbcUtils.extractDatabaseMetaData(dataSource, databaseMetaData -> {
String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());
boolean accessProcedureColumnMetaData = context.isAccessCallParameterMetaData();
if (context.isFunction()) {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 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.
Expand Down Expand Up @@ -49,9 +49,8 @@ private TableMetaDataProviderFactory() {
*/
public static TableMetaDataProvider createMetaDataProvider(DataSource dataSource, TableMetaDataContext context) {
try {
return (TableMetaDataProvider) JdbcUtils.extractDatabaseMetaData(dataSource, databaseMetaData -> {
String databaseProductName =
JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());
return JdbcUtils.extractDatabaseMetaData(dataSource, databaseMetaData -> {
String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());
boolean accessTableColumnMetaData = context.isAccessTableColumnMetaData();
TableMetaDataProvider provider;

Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2020 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.
Expand All @@ -26,10 +26,12 @@
* and handled correctly by the JdbcUtils class.
*
* @author Thomas Risberg
* @see JdbcUtils#extractDatabaseMetaData
* @author Juergen Hoeller
* @param <T> the result type
* @see JdbcUtils#extractDatabaseMetaData(javax.sql.DataSource, DatabaseMetaDataCallback)
*/
@FunctionalInterface
public interface DatabaseMetaDataCallback {
public interface DatabaseMetaDataCallback<T> {

/**
* Implementations must implement this method to process the meta-data
Expand All @@ -42,6 +44,6 @@ public interface DatabaseMetaDataCallback {
* @throws MetaDataAccessException in case of other failures while
* extracting meta-data (for example, reflection failure)
*/
Object processMetaData(DatabaseMetaData dbmd) throws SQLException, MetaDataAccessException;
T processMetaData(DatabaseMetaData dbmd) throws SQLException, MetaDataAccessException;

}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
Expand Down Expand Up @@ -315,26 +315,44 @@ else if (obj instanceof java.sql.Date) {

/**
* Extract database meta-data via the given DatabaseMetaDataCallback.
* <p>This method will open a connection to the database and retrieve the database meta-data.
* Since this method is called before the exception translation feature is configured for
* a datasource, this method can not rely on the SQLException translation functionality.
* <p>Any exceptions will be wrapped in a MetaDataAccessException. This is a checked exception
* and any calling code should catch and handle this exception. You can just log the
* error and hope for the best, but there is probably a more serious error that will
* reappear when you try to access the database again.
* <p>This method will open a connection to the database and retrieve its meta-data.
* Since this method is called before the exception translation feature is configured
* for a DataSource, this method can not rely on SQLException translation itself.
* <p>Any exceptions will be wrapped in a MetaDataAccessException. This is a checked
* exception and any calling code should catch and handle this exception. You can just
* log the error and hope for the best, but there is probably a more serious error that
* will reappear when you try to access the database again.
* @param dataSource the DataSource to extract meta-data for
* @param action callback that will do the actual work
* @return object containing the extracted information, as returned by
* the DatabaseMetaDataCallback's {@code processMetaData} method
* @throws MetaDataAccessException if meta-data access failed
* @see java.sql.DatabaseMetaData
*/
public static Object extractDatabaseMetaData(DataSource dataSource, DatabaseMetaDataCallback action)
public static <T> T extractDatabaseMetaData(DataSource dataSource, DatabaseMetaDataCallback<T> action)
throws MetaDataAccessException {

Connection con = null;
try {
con = DataSourceUtils.getConnection(dataSource);
DatabaseMetaData metaData = con.getMetaData();
DatabaseMetaData metaData;
try {
metaData = con.getMetaData();
}
catch (SQLException ex) {
if (DataSourceUtils.isConnectionTransactional(con, dataSource)) {
// Probably a closed thread-bound Connection - retry against fresh Connection
DataSourceUtils.releaseConnection(con, dataSource);
con = null;
logger.debug("Failed to obtain DatabaseMetaData from transactional Connection - " +
"retrying against fresh Connection", ex);
con = dataSource.getConnection();
metaData = con.getMetaData();
}
else {
throw ex;
}
}
if (metaData == null) {
// should only happen in test environments
throw new MetaDataAccessException("DatabaseMetaData returned by Connection [" + con + "] was null");
Expand Down Expand Up @@ -365,7 +383,11 @@ public static Object extractDatabaseMetaData(DataSource dataSource, DatabaseMeta
* @throws MetaDataAccessException if we couldn't access the DatabaseMetaData
* or failed to invoke the specified method
* @see java.sql.DatabaseMetaData
* @deprecated as of 5.2.9, in favor of
* {@link #extractDatabaseMetaData(DataSource, DatabaseMetaDataCallback)}
* with a lambda expression or method reference and a generically typed result
*/
@Deprecated
@SuppressWarnings("unchecked")
public static <T> T extractDatabaseMetaData(DataSource dataSource, final String metaDataMethodName)
throws MetaDataAccessException {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 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.
Expand Down Expand Up @@ -35,6 +35,8 @@
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.InvalidResultSetAccessException;
import org.springframework.lang.Nullable;
import org.springframework.util.function.SingletonSupplier;
import org.springframework.util.function.SupplierUtils;

/**
* Implementation of {@link SQLExceptionTranslator} that analyzes vendor-specific error codes.
Expand Down Expand Up @@ -76,7 +78,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep

/** Error codes used by this translator. */
@Nullable
private SQLErrorCodes sqlErrorCodes;
private SingletonSupplier<SQLErrorCodes> sqlErrorCodes;


/**
Expand Down Expand Up @@ -120,7 +122,7 @@ public SQLErrorCodeSQLExceptionTranslator(String dbName) {
*/
public SQLErrorCodeSQLExceptionTranslator(SQLErrorCodes sec) {
this();
this.sqlErrorCodes = sec;
this.sqlErrorCodes = SingletonSupplier.of(sec);
}


Expand All @@ -134,7 +136,9 @@ public SQLErrorCodeSQLExceptionTranslator(SQLErrorCodes sec) {
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
*/
public void setDataSource(DataSource dataSource) {
this.sqlErrorCodes = SQLErrorCodesFactory.getInstance().getErrorCodes(dataSource);
this.sqlErrorCodes =
SingletonSupplier.of(() -> SQLErrorCodesFactory.getInstance().resolveErrorCodes(dataSource));
this.sqlErrorCodes.get(); // try early initialization - otherwise the supplier will retry later
}

/**
Expand All @@ -146,15 +150,15 @@ public void setDataSource(DataSource dataSource) {
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
*/
public void setDatabaseProductName(String dbName) {
this.sqlErrorCodes = SQLErrorCodesFactory.getInstance().getErrorCodes(dbName);
this.sqlErrorCodes = SingletonSupplier.of(SQLErrorCodesFactory.getInstance().getErrorCodes(dbName));
}

/**
* Set custom error codes to be used for translation.
* @param sec custom error codes to use
*/
public void setSqlErrorCodes(@Nullable SQLErrorCodes sec) {
this.sqlErrorCodes = sec;
this.sqlErrorCodes = SingletonSupplier.ofNullable(sec);
}

/**
Expand All @@ -164,7 +168,7 @@ public void setSqlErrorCodes(@Nullable SQLErrorCodes sec) {
*/
@Nullable
public SQLErrorCodes getSqlErrorCodes() {
return this.sqlErrorCodes;
return SupplierUtils.resolve(this.sqlErrorCodes);
}


Expand All @@ -175,7 +179,6 @@ protected DataAccessException doTranslate(String task, @Nullable String sql, SQL
if (sqlEx instanceof BatchUpdateException && sqlEx.getNextException() != null) {
SQLException nestedSqlEx = sqlEx.getNextException();
if (nestedSqlEx.getErrorCode() > 0 || nestedSqlEx.getSQLState() != null) {
logger.debug("Using nested SQLException from the BatchUpdateException");
sqlEx = nestedSqlEx;
}
}
Expand All @@ -187,8 +190,9 @@ protected DataAccessException doTranslate(String task, @Nullable String sql, SQL
}

// Next, try the custom SQLException translator, if available.
if (this.sqlErrorCodes != null) {
SQLExceptionTranslator customTranslator = this.sqlErrorCodes.getCustomSqlExceptionTranslator();
SQLErrorCodes sqlErrorCodes = getSqlErrorCodes();
if (sqlErrorCodes != null) {
SQLExceptionTranslator customTranslator = sqlErrorCodes.getCustomSqlExceptionTranslator();
if (customTranslator != null) {
DataAccessException customDex = customTranslator.translate(task, sql, sqlEx);
if (customDex != null) {
Expand All @@ -198,9 +202,9 @@ protected DataAccessException doTranslate(String task, @Nullable String sql, SQL
}

// Check SQLErrorCodes with corresponding error code, if available.
if (this.sqlErrorCodes != null) {
if (sqlErrorCodes != null) {
String errorCode;
if (this.sqlErrorCodes.isUseSqlStateForTranslation()) {
if (sqlErrorCodes.isUseSqlStateForTranslation()) {
errorCode = sqlEx.getSQLState();
}
else {
Expand All @@ -215,7 +219,7 @@ protected DataAccessException doTranslate(String task, @Nullable String sql, SQL

if (errorCode != null) {
// Look for defined custom translations first.
CustomSQLErrorCodesTranslation[] customTranslations = this.sqlErrorCodes.getCustomTranslations();
CustomSQLErrorCodesTranslation[] customTranslations = sqlErrorCodes.getCustomTranslations();
if (customTranslations != null) {
for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) {
if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0 &&
Expand All @@ -230,43 +234,43 @@ protected DataAccessException doTranslate(String task, @Nullable String sql, SQL
}
}
// Next, look for grouped error codes.
if (Arrays.binarySearch(this.sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) {
if (Arrays.binarySearch(sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new BadSqlGrammarException(task, (sql != null ? sql : ""), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) {
else if (Arrays.binarySearch(sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new InvalidResultSetAccessException(task, (sql != null ? sql : ""), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDuplicateKeyCodes(), errorCode) >= 0) {
else if (Arrays.binarySearch(sqlErrorCodes.getDuplicateKeyCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DuplicateKeyException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDataIntegrityViolationCodes(), errorCode) >= 0) {
else if (Arrays.binarySearch(sqlErrorCodes.getDataIntegrityViolationCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DataIntegrityViolationException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getPermissionDeniedCodes(), errorCode) >= 0) {
else if (Arrays.binarySearch(sqlErrorCodes.getPermissionDeniedCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new PermissionDeniedDataAccessException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDataAccessResourceFailureCodes(), errorCode) >= 0) {
else if (Arrays.binarySearch(sqlErrorCodes.getDataAccessResourceFailureCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DataAccessResourceFailureException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getTransientDataAccessResourceCodes(), errorCode) >= 0) {
else if (Arrays.binarySearch(sqlErrorCodes.getTransientDataAccessResourceCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new TransientDataAccessResourceException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotAcquireLockCodes(), errorCode) >= 0) {
else if (Arrays.binarySearch(sqlErrorCodes.getCannotAcquireLockCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new CannotAcquireLockException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDeadlockLoserCodes(), errorCode) >= 0) {
else if (Arrays.binarySearch(sqlErrorCodes.getDeadlockLoserCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DeadlockLoserDataAccessException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotSerializeTransactionCodes(), errorCode) >= 0) {
else if (Arrays.binarySearch(sqlErrorCodes.getCannotSerializeTransactionCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new CannotSerializeTransactionException(buildMessage(task, sql, sqlEx), sqlEx);
}
Expand All @@ -276,7 +280,7 @@ else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotSerializeTransactionCod
// We couldn't identify it more precisely - let's hand it over to the SQLState fallback translator.
if (logger.isDebugEnabled()) {
String codes;
if (this.sqlErrorCodes != null && this.sqlErrorCodes.isUseSqlStateForTranslation()) {
if (sqlErrorCodes != null && sqlErrorCodes.isUseSqlStateForTranslation()) {
codes = "SQL state '" + sqlEx.getSQLState() + "', error code '" + sqlEx.getErrorCode();
}
else {
Expand Down

0 comments on commit 3aae0fc

Please sign in to comment.