From 5eb30621db1b84f9b260b33737cc79e8bace7b27 Mon Sep 17 00:00:00 2001 From: jjh75607 Date: Sat, 22 Aug 2026 16:44:15 +0900 Subject: [PATCH 1/2] Honor a wrapper's unwrap when resolving the root DataSource See gh-51410 Signed-off-by: jjh75607 --- .../boot/jdbc/DataSourceUnwrapper.java | 11 ++--- .../boot/jdbc/DataSourceUnwrapperTests.java | 45 +++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java b/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java index de1b3e01440..ccc976f2143 100644 --- a/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java +++ b/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java @@ -102,6 +102,10 @@ public final class DataSourceUnwrapper { * @since 4.1.0 */ public static DataSource unwrapRoot(DataSource dataSource) { + DataSource unwrapped = safeUnwrap(dataSource); + if (unwrapped != null && unwrapped != dataSource) { + return unwrapRoot(unwrapped); + } if (DELEGATING_DATA_SOURCE_PRESENT) { DataSource targetDataSource = DelegatingDataSourceUnwrapper.getTargetDataSource(dataSource); if (targetDataSource != null) { @@ -114,13 +118,6 @@ public final class DataSourceUnwrapper { return unwrapRoot(proxyDataSource); } } - DataSource unwrapped = safeUnwrap(dataSource); - if (unwrapped != null) { - if (unwrapped == dataSource) { - return unwrapped; - } - return unwrapRoot(unwrapped); - } return dataSource; } diff --git a/module/spring-boot-jdbc/src/test/java/org/springframework/boot/jdbc/DataSourceUnwrapperTests.java b/module/spring-boot-jdbc/src/test/java/org/springframework/boot/jdbc/DataSourceUnwrapperTests.java index 9a00a8dae54..c41de518acf 100644 --- a/module/spring-boot-jdbc/src/test/java/org/springframework/boot/jdbc/DataSourceUnwrapperTests.java +++ b/module/spring-boot-jdbc/src/test/java/org/springframework/boot/jdbc/DataSourceUnwrapperTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.jdbc; +import java.sql.Connection; import java.sql.SQLException; import java.util.function.Consumer; @@ -28,6 +29,7 @@ import org.apache.tomcat.jdbc.pool.PoolConfiguration; import org.junit.jupiter.api.Test; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.jdbc.datasource.AbstractDataSource; import org.springframework.jdbc.datasource.DelegatingDataSource; import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; import org.springframework.jdbc.datasource.SingleConnectionDataSource; @@ -140,6 +142,19 @@ class DataSourceUnwrapperTests { assertThat(DataSourceUnwrapper.unwrapRoot(actual)).isSameAs(dataSource); } + @Test + void unwrapRootWithSelfReturningWrapper() { + DataSource dataSource = new SelfReturningDataSource(); + assertThat(DataSourceUnwrapper.unwrapRoot(dataSource)).isSameAs(dataSource); + } + + @Test + void unwrapRootWithDelegateWrappingSelfReturningWrapper() { + DataSource dataSource = new HikariDataSource(); + DataSource actual = new RootAwareDelegatingDataSource(new SelfReturningDataSource(), dataSource); + assertThat(DataSourceUnwrapper.unwrapRoot(actual)).isSameAs(dataSource); + } + @Test void unwrappingIsNotAttemptedWhenTargetIsNotAnInterface() { DataSource dataSource = mock(DataSource.class); @@ -163,4 +178,34 @@ class DataSourceUnwrapperTests { return new DelegatingDataSource(dataSource); } + private static final class SelfReturningDataSource extends AbstractDataSource { + + @Override + public Connection getConnection() throws SQLException { + throw new UnsupportedOperationException(); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + throw new UnsupportedOperationException(); + } + + } + + private static final class RootAwareDelegatingDataSource extends DelegatingDataSource { + + private final DataSource root; + + private RootAwareDelegatingDataSource(DataSource target, DataSource root) { + super(target); + this.root = root; + } + + @Override + public T unwrap(Class iface) throws SQLException { + return (iface.isInstance(this.root)) ? iface.cast(this.root) : super.unwrap(iface); + } + + } + } From d5cec292dc1a3ad51f8b321253f9d8956ecf06eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 24 Aug 2026 10:39:07 +0200 Subject: [PATCH 2/2] Polish "Honor a wrapper's unwrap when resolving the root DataSource" See gh-51410 --- .../org/springframework/boot/jdbc/DataSourceUnwrapper.java | 7 ++++--- .../boot/jdbc/DataSourceUnwrapperTests.java | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java b/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java index ccc976f2143..d5a89a380bd 100644 --- a/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java +++ b/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java @@ -94,9 +94,10 @@ public final class DataSourceUnwrapper { } /** - * Return the root {@link DataSource} by recursively unwrapping all - * {@link org.springframework.jdbc.datasource.DelegatingDataSource delegating}, proxy, - * and {@link java.sql.Wrapper} layers until no further unwrapping is possible. + * Return the root {@link DataSource} by recursively unwrapping + * {@link java.sql.Wrapper}, + * {@link org.springframework.jdbc.datasource.DelegatingDataSource delegating}, and + * proxy layers until no further unwrapping is possible. * @param dataSource the datasource to unwrap * @return the root {@link DataSource} * @since 4.1.0 diff --git a/module/spring-boot-jdbc/src/test/java/org/springframework/boot/jdbc/DataSourceUnwrapperTests.java b/module/spring-boot-jdbc/src/test/java/org/springframework/boot/jdbc/DataSourceUnwrapperTests.java index c41de518acf..6656af39c53 100644 --- a/module/spring-boot-jdbc/src/test/java/org/springframework/boot/jdbc/DataSourceUnwrapperTests.java +++ b/module/spring-boot-jdbc/src/test/java/org/springframework/boot/jdbc/DataSourceUnwrapperTests.java @@ -181,12 +181,12 @@ class DataSourceUnwrapperTests { private static final class SelfReturningDataSource extends AbstractDataSource { @Override - public Connection getConnection() throws SQLException { + public Connection getConnection() { throw new UnsupportedOperationException(); } @Override - public Connection getConnection(String username, String password) throws SQLException { + public Connection getConnection(String username, String password) { throw new UnsupportedOperationException(); }