Use SQLExceptionSubclassTranslator by default (avoiding sql-error-codes.xml)
SQLErrorCodeSQLExceptionTranslator kicks in for user-provided sql-error-codes.xml files. It will still pick up Spring's legacy default error code mappings as well but only when triggered by a (potentially empty) user-provided file in the root of the classpath. Closes gh-28216
This commit is contained in:
@@ -22,7 +22,6 @@ import org.apache.commons.logging.Log;
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.core.SpringProperties;
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@@ -41,13 +40,6 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public abstract class JdbcAccessor implements InitializingBean {
|
public abstract class JdbcAccessor implements InitializingBean {
|
||||||
|
|
||||||
/**
|
|
||||||
* Boolean flag controlled by a {@code spring.xml.ignore} system property that instructs Spring to
|
|
||||||
* ignore XML, i.e. to not initialize the XML-related infrastructure.
|
|
||||||
* <p>The default is "false".
|
|
||||||
*/
|
|
||||||
private static final boolean shouldIgnoreXml = SpringProperties.getFlag("spring.xml.ignore");
|
|
||||||
|
|
||||||
/** Logger available to subclasses. */
|
/** Logger available to subclasses. */
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
@@ -96,9 +88,12 @@ public abstract class JdbcAccessor implements InitializingBean {
|
|||||||
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
|
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
|
||||||
*/
|
*/
|
||||||
public void setDatabaseProductName(String dbName) {
|
public void setDatabaseProductName(String dbName) {
|
||||||
if (!shouldIgnoreXml) {
|
if (SQLErrorCodeSQLExceptionTranslator.hasUserProvidedErrorCodesFile()) {
|
||||||
this.exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dbName);
|
this.exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dbName);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.exceptionTranslator = new SQLExceptionSubclassTranslator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,15 +123,11 @@ public abstract class JdbcAccessor implements InitializingBean {
|
|||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
exceptionTranslator = this.exceptionTranslator;
|
exceptionTranslator = this.exceptionTranslator;
|
||||||
if (exceptionTranslator == null) {
|
if (exceptionTranslator == null) {
|
||||||
DataSource dataSource = getDataSource();
|
if (SQLErrorCodeSQLExceptionTranslator.hasUserProvidedErrorCodesFile()) {
|
||||||
if (shouldIgnoreXml) {
|
exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(obtainDataSource());
|
||||||
exceptionTranslator = new SQLExceptionSubclassTranslator();
|
|
||||||
}
|
|
||||||
else if (dataSource != null) {
|
|
||||||
exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
exceptionTranslator = new SQLStateSQLExceptionTranslator();
|
exceptionTranslator = new SQLExceptionSubclassTranslator();
|
||||||
}
|
}
|
||||||
this.exceptionTranslator = exceptionTranslator;
|
this.exceptionTranslator = exceptionTranslator;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 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.
|
||||||
@@ -20,7 +20,6 @@ import java.sql.SQLException;
|
|||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.springframework.core.SpringProperties;
|
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
@@ -53,14 +52,6 @@ import org.springframework.lang.Nullable;
|
|||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class JdbcTransactionManager extends DataSourceTransactionManager {
|
public class JdbcTransactionManager extends DataSourceTransactionManager {
|
||||||
|
|
||||||
/**
|
|
||||||
* Boolean flag controlled by a {@code spring.xml.ignore} system property that instructs Spring to
|
|
||||||
* ignore XML, i.e. to not initialize the XML-related infrastructure.
|
|
||||||
* <p>The default is "false".
|
|
||||||
*/
|
|
||||||
private static final boolean shouldIgnoreXml = SpringProperties.getFlag("spring.xml.ignore");
|
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private volatile SQLExceptionTranslator exceptionTranslator;
|
private volatile SQLExceptionTranslator exceptionTranslator;
|
||||||
|
|
||||||
@@ -97,9 +88,12 @@ public class JdbcTransactionManager extends DataSourceTransactionManager {
|
|||||||
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
|
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
|
||||||
*/
|
*/
|
||||||
public void setDatabaseProductName(String dbName) {
|
public void setDatabaseProductName(String dbName) {
|
||||||
if (!shouldIgnoreXml) {
|
if (SQLErrorCodeSQLExceptionTranslator.hasUserProvidedErrorCodesFile()) {
|
||||||
this.exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dbName);
|
this.exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dbName);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.exceptionTranslator = new SQLExceptionSubclassTranslator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,11 +122,11 @@ public class JdbcTransactionManager extends DataSourceTransactionManager {
|
|||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
exceptionTranslator = this.exceptionTranslator;
|
exceptionTranslator = this.exceptionTranslator;
|
||||||
if (exceptionTranslator == null) {
|
if (exceptionTranslator == null) {
|
||||||
if (shouldIgnoreXml) {
|
if (SQLErrorCodeSQLExceptionTranslator.hasUserProvidedErrorCodesFile()) {
|
||||||
exceptionTranslator = new SQLExceptionSubclassTranslator();
|
exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(obtainDataSource());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(obtainDataSource());
|
exceptionTranslator = new SQLExceptionSubclassTranslator();
|
||||||
}
|
}
|
||||||
this.exceptionTranslator = exceptionTranslator;
|
this.exceptionTranslator = exceptionTranslator;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.dao.CannotAcquireLockException;
|
import org.springframework.dao.CannotAcquireLockException;
|
||||||
import org.springframework.dao.CannotSerializeTransactionException;
|
import org.springframework.dao.CannotSerializeTransactionException;
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
@@ -417,4 +418,14 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether there is a user-provided `sql-error-codes.xml` file
|
||||||
|
* in the root of the classpath.
|
||||||
|
*/
|
||||||
|
static boolean hasUserProvidedErrorCodesFile() {
|
||||||
|
return new ClassPathResource(SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH,
|
||||||
|
SQLErrorCodesFactory.class.getClassLoader()).exists();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 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.
|
||||||
@@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
|
|||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||||
|
import org.springframework.core.SpringProperties;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
@@ -66,6 +67,13 @@ public class SQLErrorCodesFactory {
|
|||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(SQLErrorCodesFactory.class);
|
private static final Log logger = LogFactory.getLog(SQLErrorCodesFactory.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean flag controlled by a {@code spring.xml.ignore} system property that instructs Spring to
|
||||||
|
* ignore XML, i.e. to not initialize the XML-related infrastructure.
|
||||||
|
* <p>The default is "false".
|
||||||
|
*/
|
||||||
|
private static final boolean shouldIgnoreXml = SpringProperties.getFlag("spring.xml.ignore");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep track of a single instance so we can return it to classes that request it.
|
* Keep track of a single instance so we can return it to classes that request it.
|
||||||
*/
|
*/
|
||||||
@@ -101,6 +109,10 @@ public class SQLErrorCodesFactory {
|
|||||||
* @see #loadResource(String)
|
* @see #loadResource(String)
|
||||||
*/
|
*/
|
||||||
protected SQLErrorCodesFactory() {
|
protected SQLErrorCodesFactory() {
|
||||||
|
if (shouldIgnoreXml) {
|
||||||
|
throw new UnsupportedOperationException("XML support disabled");
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, SQLErrorCodes> errorCodes;
|
Map<String, SQLErrorCodes> errorCodes;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -944,6 +944,8 @@ public class JdbcTemplateTests {
|
|||||||
mockDatabaseMetaData(false);
|
mockDatabaseMetaData(false);
|
||||||
given(this.connection.createStatement()).willReturn(this.preparedStatement);
|
given(this.connection.createStatement()).willReturn(this.preparedStatement);
|
||||||
|
|
||||||
|
this.template.setExceptionTranslator(new SQLErrorCodeSQLExceptionTranslator(this.dataSource));
|
||||||
|
|
||||||
assertThatExceptionOfType(BadSqlGrammarException.class).isThrownBy(() ->
|
assertThatExceptionOfType(BadSqlGrammarException.class).isThrownBy(() ->
|
||||||
this.template.query(sql, (RowCallbackHandler) rs -> {
|
this.template.query(sql, (RowCallbackHandler) rs -> {
|
||||||
throw sqlException;
|
throw sqlException;
|
||||||
@@ -955,20 +957,17 @@ public class JdbcTemplateTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSQLErrorCodeTranslationWithSpecifiedDbName() throws Exception {
|
public void testSQLErrorCodeTranslationWithSpecifiedDatabaseName() throws Exception {
|
||||||
final SQLException sqlException = new SQLException("I have a known problem", "99999", 1054);
|
final SQLException sqlException = new SQLException("I have a known problem", "99999", 1054);
|
||||||
final String sql = "SELECT ID FROM CUSTOMER";
|
final String sql = "SELECT ID FROM CUSTOMER";
|
||||||
|
|
||||||
given(this.resultSet.next()).willReturn(true);
|
given(this.resultSet.next()).willReturn(true);
|
||||||
given(this.connection.createStatement()).willReturn(this.preparedStatement);
|
given(this.connection.createStatement()).willReturn(this.preparedStatement);
|
||||||
|
|
||||||
JdbcTemplate template = new JdbcTemplate();
|
this.template.setExceptionTranslator(new SQLErrorCodeSQLExceptionTranslator("MySQL"));
|
||||||
template.setDataSource(this.dataSource);
|
|
||||||
template.setDatabaseProductName("MySQL");
|
|
||||||
template.afterPropertiesSet();
|
|
||||||
|
|
||||||
assertThatExceptionOfType(BadSqlGrammarException.class).isThrownBy(() ->
|
assertThatExceptionOfType(BadSqlGrammarException.class).isThrownBy(() ->
|
||||||
template.query(sql, (RowCallbackHandler) rs -> {
|
this.template.query(sql, (RowCallbackHandler) rs -> {
|
||||||
throw sqlException;
|
throw sqlException;
|
||||||
}))
|
}))
|
||||||
.withCause(sqlException);
|
.withCause(sqlException);
|
||||||
@@ -983,7 +982,7 @@ public class JdbcTemplateTests {
|
|||||||
* to get the metadata
|
* to get the metadata
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testUseCustomSQLErrorCodeTranslator() throws Exception {
|
public void testUseCustomExceptionTranslator() throws Exception {
|
||||||
// Bad SQL state
|
// Bad SQL state
|
||||||
final SQLException sqlException = new SQLException("I have a known problem", "07000", 1054);
|
final SQLException sqlException = new SQLException("I have a known problem", "07000", 1054);
|
||||||
final String sql = "SELECT ID FROM CUSTOMER";
|
final String sql = "SELECT ID FROM CUSTOMER";
|
||||||
|
|||||||
Reference in New Issue
Block a user