From 5f5172876146e1f5bebafb62cdd388f98cb3056a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 3 Sep 2020 23:21:56 +0200 Subject: [PATCH] Retry DatabaseMetaData retrieval if access to transactional connection fails Closes gh-25681 --- .../jdbc/support/JdbcUtils.java | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcUtils.java index 33cd14aaab61..cb09d90e0680 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcUtils.java @@ -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. @@ -298,18 +298,19 @@ else if (obj instanceof java.sql.Date) { /** * Extract database meta-data via the given DatabaseMetaDataCallback. - *

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. - *

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. + *

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. + *

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) throws MetaDataAccessException { @@ -317,7 +318,24 @@ public static Object extractDatabaseMetaData(DataSource dataSource, DatabaseMeta 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"); @@ -446,9 +464,9 @@ public static boolean isNumeric(int sqlType) { * expressed in the JDBC 4.0 specification: *

columnLabel - the label for the column specified with the SQL AS clause. * If the SQL AS clause was not specified, then the label is the name of the column. - * @return the column name to use * @param resultSetMetaData the current meta-data to use * @param columnIndex the index of the column for the look up + * @return the column name to use * @throws SQLException in case of lookup failure */ public static String lookupColumnName(ResultSetMetaData resultSetMetaData, int columnIndex) throws SQLException {