Browse Source

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
pull/28631/head
Juergen Hoeller 4 years ago
parent
commit
083113d8a4
  1. 23
      spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcAccessor.java
  2. 22
      spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java
  3. 11
      spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java
  4. 14
      spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java
  5. 13
      spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java

23
spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcAccessor.java

@ -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;
} }

22
spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java

@ -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;
} }

11
spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java

@ -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();
}
} }

14
spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java

@ -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 {

13
spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java

@ -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";

Loading…
Cancel
Save