Merge branch '6.2.x'

This commit is contained in:
Juergen Hoeller 2025-06-18 12:23:07 +02:00
commit 6927e39e6b
5 changed files with 36 additions and 21 deletions

View File

@ -453,7 +453,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
() -> { () -> {
CompletableFuture<?> invokeResult = ((CompletableFuture<?>) invokeOperation(invoker)); CompletableFuture<?> invokeResult = ((CompletableFuture<?>) invokeOperation(invoker));
if (invokeResult == null) { if (invokeResult == null) {
return null; throw new IllegalStateException("Returned CompletableFuture must not be null: " + method);
} }
return invokeResult.exceptionallyCompose(ex -> { return invokeResult.exceptionallyCompose(ex -> {
invokeFailure.set(true); invokeFailure.set(true);

View File

@ -130,15 +130,14 @@ public class ReflectiveRuntimeHintsRegistrar {
private ReflectiveProcessor instantiateClass(Class<? extends ReflectiveProcessor> type) { private ReflectiveProcessor instantiateClass(Class<? extends ReflectiveProcessor> type) {
try { try {
Constructor<? extends ReflectiveProcessor> constructor = type.getDeclaredConstructor(); return ReflectionUtils.accessibleConstructor(type).newInstance();
ReflectionUtils.makeAccessible(constructor);
return constructor.newInstance();
} }
catch (Exception ex) { catch (Exception ex) {
throw new IllegalStateException("Failed to instantiate " + type, ex); throw new IllegalStateException("Failed to instantiate " + type, ex);
} }
} }
private static class DelegatingReflectiveProcessor implements ReflectiveProcessor { private static class DelegatingReflectiveProcessor implements ReflectiveProcessor {
private final Iterable<ReflectiveProcessor> processors; private final Iterable<ReflectiveProcessor> processors;
@ -151,9 +150,10 @@ public class ReflectiveRuntimeHintsRegistrar {
public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) { public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) {
this.processors.forEach(processor -> processor.registerReflectionHints(hints, element)); this.processors.forEach(processor -> processor.registerReflectionHints(hints, element));
} }
} }
private record Entry(AnnotatedElement element, ReflectiveProcessor processor) {}
private record Entry(AnnotatedElement element, ReflectiveProcessor processor) {
}
} }

View File

@ -18,7 +18,6 @@ package org.springframework.core.io.support;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.SocketException; import java.net.SocketException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
@ -147,9 +146,7 @@ public class PropertySourceProcessor {
private static PropertySourceFactory instantiateClass(Class<? extends PropertySourceFactory> type) { private static PropertySourceFactory instantiateClass(Class<? extends PropertySourceFactory> type) {
try { try {
Constructor<? extends PropertySourceFactory> constructor = type.getDeclaredConstructor(); return ReflectionUtils.accessibleConstructor(type).newInstance();
ReflectionUtils.makeAccessible(constructor);
return constructor.newInstance();
} }
catch (Exception ex) { catch (Exception ex) {
throw new IllegalStateException("Failed to instantiate " + type, ex); throw new IllegalStateException("Failed to instantiate " + type, ex);

View File

@ -71,6 +71,11 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException
"44" // With check violation "44" // With check violation
); );
private static final Set<String> PESSIMISTIC_LOCKING_FAILURE_CODES = Set.of(
"40", // Transaction rollback
"61" // Oracle: deadlock
);
private static final Set<String> DATA_ACCESS_RESOURCE_FAILURE_CODES = Set.of( private static final Set<String> DATA_ACCESS_RESOURCE_FAILURE_CODES = Set.of(
"08", // Connection exception "08", // Connection exception
"53", // PostgreSQL: insufficient resources (for example, disk full) "53", // PostgreSQL: insufficient resources (for example, disk full)
@ -85,11 +90,6 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException
"S1" // DB2: communication failure "S1" // DB2: communication failure
); );
private static final Set<String> PESSIMISTIC_LOCKING_FAILURE_CODES = Set.of(
"40", // Transaction rollback
"61" // Oracle: deadlock
);
private static final Set<Integer> DUPLICATE_KEY_ERROR_CODES = Set.of( private static final Set<Integer> DUPLICATE_KEY_ERROR_CODES = Set.of(
1, // Oracle 1, // Oracle
301, // SAP HANA 301, // SAP HANA
@ -117,18 +117,21 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException
} }
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex); return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
} }
else if (DATA_ACCESS_RESOURCE_FAILURE_CODES.contains(classCode)) {
return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex);
}
else if (TRANSIENT_DATA_ACCESS_RESOURCE_CODES.contains(classCode)) {
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
}
else if (PESSIMISTIC_LOCKING_FAILURE_CODES.contains(classCode)) { else if (PESSIMISTIC_LOCKING_FAILURE_CODES.contains(classCode)) {
if (indicatesCannotAcquireLock(sqlState)) { if (indicatesCannotAcquireLock(sqlState)) {
return new CannotAcquireLockException(buildMessage(task, sql, ex), ex); return new CannotAcquireLockException(buildMessage(task, sql, ex), ex);
} }
return new PessimisticLockingFailureException(buildMessage(task, sql, ex), ex); return new PessimisticLockingFailureException(buildMessage(task, sql, ex), ex);
} }
else if (DATA_ACCESS_RESOURCE_FAILURE_CODES.contains(classCode)) {
if (indicatesQueryTimeout(sqlState)) {
return new QueryTimeoutException(buildMessage(task, sql, ex), ex);
}
return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex);
}
else if (TRANSIENT_DATA_ACCESS_RESOURCE_CODES.contains(classCode)) {
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
}
} }
// For MySQL: exception class name indicating a timeout? // For MySQL: exception class name indicating a timeout?
@ -183,4 +186,13 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException
return "40001".equals(sqlState); return "40001".equals(sqlState);
} }
/**
* Check whether the given SQL state indicates a {@link QueryTimeoutException},
* with SQL state 57014 as a specific indication.
* @param sqlState the SQL state value
*/
static boolean indicatesQueryTimeout(@Nullable String sqlState) {
return "57014".equals(sqlState);
}
} }

View File

@ -27,6 +27,7 @@ import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.PessimisticLockingFailureException; import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.dao.QueryTimeoutException;
import org.springframework.dao.TransientDataAccessResourceException; import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.BadSqlGrammarException;
@ -109,6 +110,11 @@ class SQLStateSQLExceptionTranslatorTests {
assertTranslation("40001", CannotAcquireLockException.class); assertTranslation("40001", CannotAcquireLockException.class);
} }
@Test
void translateQueryTimeout() {
assertTranslation("57014", QueryTimeoutException.class);
}
@Test @Test
void translateUncategorized() { void translateUncategorized() {
assertTranslation("00000000", null); assertTranslation("00000000", null);