diff --git a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java index 695c760d6f8..2fb7bed7f99 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java @@ -354,7 +354,7 @@ public class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setBeans(beansToExport); exporter.setServer(getServer()); exporter.setBeanFactory(factory); - exporter.setAutodetectMode(MBeanExporter.AUTODETECT_NONE); + exporter.setAutodetect(false); // MBean has a bad ObjectName, so if said MBean is autodetected, an exception will be thrown... start(exporter); } @@ -524,18 +524,16 @@ public class MBeanExporterTests extends AbstractMBeanServerTests { } @Test - void notRunningInBeanFactoryAndPassedBeanNameToExport() throws Exception { + void notRunningInBeanFactoryAndPassedBeanNameToExport() { Map beans = Map.of(OBJECT_NAME, "beanName"); exporter.setBeans(beans); - assertThatExceptionOfType(MBeanExportException.class) - .isThrownBy(() -> start(exporter)); + assertThatExceptionOfType(MBeanExportException.class).isThrownBy(() -> start(exporter)); } @Test - void notRunningInBeanFactoryAndAutodetectionIsOn() throws Exception { - exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL); - assertThatExceptionOfType(MBeanExportException.class) - .isThrownBy(() -> start(exporter)); + void notRunningInBeanFactoryAndAutodetectionIsOn() { + exporter.setAutodetect(true); + assertThatExceptionOfType(MBeanExportException.class).isThrownBy(() -> start(exporter)); } @Test // SPR-2158 @@ -556,7 +554,7 @@ public class MBeanExporterTests extends AbstractMBeanServerTests { } @Test // SPR-3302 - void beanNameCanBeUsedInNotificationListenersMap() throws Exception { + void beanNameCanBeUsedInNotificationListenersMap() { String beanName = "charlesDexterWard"; BeanDefinitionBuilder testBean = BeanDefinitionBuilder.rootBeanDefinition(JmxTestBean.class); @@ -576,7 +574,7 @@ public class MBeanExporterTests extends AbstractMBeanServerTests { } @Test - void wildcardCanBeUsedInNotificationListenersMap() throws Exception { + void wildcardCanBeUsedInNotificationListenersMap() { String beanName = "charlesDexterWard"; BeanDefinitionBuilder testBean = BeanDefinitionBuilder.rootBeanDefinition(JmxTestBean.class); @@ -636,24 +634,23 @@ public class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setServer(getServer()); exporter.setAssembler(new NamedBeanAutodetectCapableMBeanInfoAssemblerStub(firstBeanName, secondBeanName)); exporter.setBeanFactory(factory); - exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL); + exporter.setAutodetect(true); exporter.addExcludedBean(secondBeanName); start(exporter); - assertIsRegistered("Bean not autodetected in (AUTODETECT_ALL) mode", - ObjectNameManager.getInstance(firstBeanName)); - assertIsNotRegistered("Bean should have been excluded", - ObjectNameManager.getInstance(secondBeanName)); + assertIsRegistered("Bean not autodetected", ObjectNameManager.getInstance(firstBeanName)); + assertIsNotRegistered("Bean should have been excluded", ObjectNameManager.getInstance(secondBeanName)); } @Test void registerFactoryBean() throws MalformedObjectNameException { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - factory.registerBeanDefinition("spring:type=FactoryBean", new RootBeanDefinition(ProperSomethingFactoryBean.class)); + factory.registerBeanDefinition("spring:type=FactoryBean", + new RootBeanDefinition(ProperSomethingFactoryBean.class)); exporter.setServer(getServer()); exporter.setBeanFactory(factory); - exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL); + exporter.setAutodetect(true); start(exporter); assertIsRegistered("Non-null FactoryBean object registered", @@ -663,11 +660,12 @@ public class MBeanExporterTests extends AbstractMBeanServerTests { @Test void ignoreNullObjectFromFactoryBean() throws MalformedObjectNameException { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - factory.registerBeanDefinition("spring:type=FactoryBean", new RootBeanDefinition(NullSomethingFactoryBean.class)); + factory.registerBeanDefinition("spring:type=FactoryBean", + new RootBeanDefinition(NullSomethingFactoryBean.class)); exporter.setServer(getServer()); exporter.setBeanFactory(factory); - exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL); + exporter.setAutodetect(true); start(exporter); assertIsNotRegistered("Null FactoryBean object not registered", diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java index 44b8838b210..f1546734ac1 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -31,6 +31,8 @@ import org.springframework.util.Assert; * * @author Juergen Hoeller * @since 2.5.6 + * @see #doTranslate + * @see #setFallbackTranslator */ public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExceptionTranslator { @@ -42,8 +44,8 @@ public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExcep /** - * Override the default SQL state fallback translator - * (typically a {@link SQLStateSQLExceptionTranslator}). + * Set the fallback translator to use when this translator cannot find a + * specific match itself. */ public void setFallbackTranslator(@Nullable SQLExceptionTranslator fallback) { this.fallbackTranslator = fallback; @@ -51,6 +53,7 @@ public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExcep /** * Return the fallback exception translator, if any. + * @see #setFallbackTranslator */ @Nullable public SQLExceptionTranslator getFallbackTranslator() { diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcAccessor.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcAccessor.java index 8b4bd9c304f..5178b4a0f4e 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcAccessor.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -80,10 +80,11 @@ public abstract class JdbcAccessor implements InitializingBean { } /** - * Specify the database product name for the DataSource that this accessor uses. - * This allows to initialize an SQLErrorCodeSQLExceptionTranslator without - * obtaining a Connection from the DataSource to get the meta-data. + * Specify the database product name for the {@code DataSource} that this accessor uses. + * This allows for initializing a {@link SQLErrorCodeSQLExceptionTranslator} without + * obtaining a {@code Connection} from the {@code DataSource} to get the meta-data. * @param dbName the database product name that identifies the error codes entry + * @see #setExceptionTranslator * @see SQLErrorCodeSQLExceptionTranslator#setDatabaseProductName * @see java.sql.DatabaseMetaData#getDatabaseProductName() */ @@ -98,22 +99,20 @@ public abstract class JdbcAccessor implements InitializingBean { /** * Set the exception translator for this instance. - *

If no custom translator is provided, a default - * {@link SQLErrorCodeSQLExceptionTranslator} is used - * which examines the SQLException's vendor-specific error code. + *

A {@link SQLErrorCodeSQLExceptionTranslator} used by default if a user-provided + * `sql-error-codes.xml` file has been found in the root of the classpath. Otherwise, + * {@link SQLExceptionSubclassTranslator} serves as the default translator as of 6.0. * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator - * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator + * @see org.springframework.jdbc.support.SQLExceptionSubclassTranslator */ public void setExceptionTranslator(SQLExceptionTranslator exceptionTranslator) { this.exceptionTranslator = exceptionTranslator; } /** - * Return the exception translator for this instance. - *

Creates a default {@link SQLErrorCodeSQLExceptionTranslator} - * for the specified DataSource if none set, or a - * {@link SQLStateSQLExceptionTranslator} in case of no DataSource. - * @see #getDataSource() + * Return the exception translator to use for this instance, + * creating a default if necessary. + * @see #setExceptionTranslator */ public SQLExceptionTranslator getExceptionTranslator() { SQLExceptionTranslator exceptionTranslator = this.exceptionTranslator; diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java index bda750f4722..e03987499a1 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -59,8 +59,8 @@ public class JdbcTransactionManager extends DataSourceTransactionManager { /** - * Create a new JdbcTransactionManager instance. - * A DataSource has to be set to be able to use it. + * Create a new {@code JdbcTransactionManager} instance. + * A {@code DataSource} has to be set to be able to use it. * @see #setDataSource */ public JdbcTransactionManager() { @@ -68,7 +68,7 @@ public class JdbcTransactionManager extends DataSourceTransactionManager { } /** - * Create a new JdbcTransactionManager instance. + * Create a new {@code JdbcTransactionManager} instance. * @param dataSource the JDBC DataSource to manage transactions for */ public JdbcTransactionManager(DataSource dataSource) { @@ -79,13 +79,15 @@ public class JdbcTransactionManager extends DataSourceTransactionManager { /** - * Specify the database product name for the DataSource that this transaction manager - * uses. This allows to initialize an SQLErrorCodeSQLExceptionTranslator without - * obtaining a Connection from the DataSource to get the meta-data. + * Specify the database product name for the {@code DataSource} that this + * transaction manager operates on. + * This allows for initializing a {@link SQLErrorCodeSQLExceptionTranslator} without + * obtaining a {@code Connection} from the {@code DataSource} to get the meta-data. * @param dbName the database product name that identifies the error codes entry - * @see JdbcAccessor#setDatabaseProductName + * @see #setExceptionTranslator * @see SQLErrorCodeSQLExceptionTranslator#setDatabaseProductName * @see java.sql.DatabaseMetaData#getDatabaseProductName() + * @see JdbcAccessor#setDatabaseProductName */ public void setDatabaseProductName(String dbName) { if (SQLErrorCodeSQLExceptionTranslator.hasUserProvidedErrorCodesFile()) { @@ -97,22 +99,22 @@ public class JdbcTransactionManager extends DataSourceTransactionManager { } /** - * Set the exception translator for this instance. - *

If no custom translator is provided, a default - * {@link SQLErrorCodeSQLExceptionTranslator} is used - * which examines the SQLException's vendor-specific error code. - * @see JdbcAccessor#setExceptionTranslator + * Set the exception translator for this transaction manager. + *

A {@link SQLErrorCodeSQLExceptionTranslator} used by default if a user-provided + * `sql-error-codes.xml` file has been found in the root of the classpath. Otherwise, + * {@link SQLExceptionSubclassTranslator} serves as the default translator as of 6.0. * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator + * @see org.springframework.jdbc.support.SQLExceptionSubclassTranslator + * @see JdbcAccessor#setExceptionTranslator */ public void setExceptionTranslator(SQLExceptionTranslator exceptionTranslator) { this.exceptionTranslator = exceptionTranslator; } /** - * Return the exception translator for this instance. - *

Creates a default {@link SQLErrorCodeSQLExceptionTranslator} - * for the specified DataSource if none set. - * @see #getDataSource() + * Return the exception translator to use for this instance, + * creating a default if necessary. + * @see #setExceptionTranslator */ public SQLExceptionTranslator getExceptionTranslator() { SQLExceptionTranslator exceptionTranslator = this.exceptionTranslator; diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java index 25d1513a782..bdc7656cc57 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java @@ -60,6 +60,11 @@ import org.springframework.util.function.SupplierUtils; * of the class path (e.g. in the "/WEB-INF/classes" directory), as long as the * Spring JDBC package is loaded from the same ClassLoader. * + *

This translator is commonly used by default if a user-provided `sql-error-codes.xml` + * file has been found in the root of the classpath, as a signal to use this strategy. + * Otherwise, {@link SQLExceptionSubclassTranslator} serves as the default translator + * as of 6.0. + * * @author Rod Johnson * @author Thomas Risberg * @author Juergen Hoeller @@ -75,11 +80,10 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep private static final int MESSAGE_SQL_THROWABLE_CONSTRUCTOR = 4; private static final int MESSAGE_SQL_SQLEX_CONSTRUCTOR = 5; - private static final boolean USER_PROVIDED_ERROR_CODES_FILE_PRESENT = - new ClassPathResource(SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH, SQLErrorCodesFactory.class.getClassLoader()).exists(); - + private static final boolean userProvidedErrorCodesFilePresent = + new ClassPathResource(SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH, + SQLErrorCodesFactory.class.getClassLoader()).exists(); - /** Error codes used by this translator. */ @Nullable private SingletonSupplier sqlErrorCodes; @@ -198,9 +202,9 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep if (sqlErrorCodes != null) { SQLExceptionTranslator customTranslator = sqlErrorCodes.getCustomSqlExceptionTranslator(); if (customTranslator != null) { - DataAccessException customDex = customTranslator.translate(task, sql, sqlEx); - if (customDex != null) { - return customDex; + dae = customTranslator.translate(task, sql, sqlEx); + if (dae != null) { + return dae; } } } @@ -228,11 +232,10 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) { if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0 && customTranslation.getExceptionClass() != null) { - DataAccessException customException = createCustomException( - task, sql, sqlEx, customTranslation.getExceptionClass()); - if (customException != null) { + dae = createCustomException(task, sql, sqlEx, customTranslation.getExceptionClass()); + if (dae != null) { logTranslation(task, sql, sqlEx, true); - return customException; + return dae; } } } @@ -426,7 +429,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep * in the root of the classpath. */ static boolean hasUserProvidedErrorCodesFile() { - return USER_PROVIDED_ERROR_CODES_FILE_PRESENT; + return userProvidedErrorCodesFilePresent; } } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java index 058348f7ecb..eb5fdcd2502 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -51,6 +51,8 @@ import org.springframework.lang.Nullable; *

Falls back to a standard {@link SQLStateSQLExceptionTranslator} if the JDBC * driver does not actually expose JDBC 4 compliant {@code SQLException} subclasses. * + *

This translator serves as the default translator as of 6.0. + * * @author Thomas Risberg * @author Juergen Hoeller * @since 2.5 @@ -72,7 +74,7 @@ public class SQLExceptionSubclassTranslator extends AbstractFallbackSQLException return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex); } if (ex instanceof SQLTransactionRollbackException) { - if ("40001".equals(ex.getSQLState())) { + if (SQLStateSQLExceptionTranslator.indicatesCannotAcquireLock(ex.getSQLState())) { return new CannotAcquireLockException(buildMessage(task, sql, ex), ex); } return new PessimisticLockingFailureException(buildMessage(task, sql, ex), ex); diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java index f8d64b94859..5954618b3ea 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java @@ -39,11 +39,16 @@ import org.springframework.lang.Nullable; * does not require special initialization (no database vendor detection, etc.). * For more precise translation, consider {@link SQLErrorCodeSQLExceptionTranslator}. * + *

This translator is commonly used as a {@link #setFallbackTranslator fallback} + * behind a primary translator such as {@link SQLErrorCodeSQLExceptionTranslator} or + * {@link SQLExceptionSubclassTranslator}. + * * @author Rod Johnson * @author Juergen Hoeller * @author Thomas Risberg * @see java.sql.SQLException#getSQLState() * @see SQLErrorCodeSQLExceptionTranslator + * @see SQLExceptionSubclassTranslator */ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLExceptionTranslator { @@ -111,7 +116,7 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex); } else if (PESSIMISTIC_LOCKING_FAILURE_CODES.contains(classCode)) { - if ("40001".equals(sqlState)) { + if (indicatesCannotAcquireLock(sqlState)) { return new CannotAcquireLockException(buildMessage(task, sql, ex), ex); } return new PessimisticLockingFailureException(buildMessage(task, sql, ex), ex); @@ -148,9 +153,10 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException return sqlState; } + /** * Check whether the given SQL state (and the associated error code in case - * of a generic SQL state value) indicate a duplicate key exception: + * of a generic SQL state value) indicate a {@link DuplicateKeyException}: * either SQL state 23505 as a specific indication, or the generic SQL state * 23000 with well-known vendor codes (1 for Oracle, 1062 for MySQL/MariaDB, * 2601/2627 for MS SQL Server). @@ -163,4 +169,13 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException (errorCode == 1 || errorCode == 1062 || errorCode == 2601 || errorCode == 2627))); } + /** + * Check whether the given SQL state indicates a {@link CannotAcquireLockException}, + * with SQL state 40001 as a specific indication. + * @param sqlState the SQL state value + */ + static boolean indicatesCannotAcquireLock(@Nullable String sqlState) { + return "40001".equals(sqlState); + } + }