Skip to content

Commit 38dbc64

Browse files
committed
Add auto-configuration for JdbcClient
Closes gh-36579
1 parent 5544ccd commit 38dbc64

File tree

7 files changed

+198
-5
lines changed

7 files changed

+198
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jdbc;
18+
19+
import org.springframework.boot.autoconfigure.AutoConfiguration;
20+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
21+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
23+
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.context.annotation.Import;
26+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
27+
import org.springframework.jdbc.core.simple.JdbcClient;
28+
29+
/**
30+
* {@link EnableAutoConfiguration Auto-configuration} for {@link JdbcClient}.
31+
*
32+
* @author Stephane Nicoll
33+
* @since 3.2.0
34+
*/
35+
@AutoConfiguration(after = JdbcTemplateAutoConfiguration.class)
36+
@ConditionalOnSingleCandidate(NamedParameterJdbcTemplate.class)
37+
@ConditionalOnMissingBean(JdbcClient.class)
38+
@Import(DatabaseInitializationDependencyConfigurer.class)
39+
public class JdbcClientAutoConfiguration {
40+
41+
@Bean
42+
JdbcClient jdbcClient(NamedParameterJdbcTemplate jdbcTemplate) {
43+
return JdbcClient.create(jdbcTemplate);
44+
}
45+
46+
}

spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
6868
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration
6969
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
7070
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
71+
org.springframework.boot.autoconfigure.jdbc.JdbcClientAutoConfiguration
7172
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
7273
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration
7374
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jdbc;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.boot.autoconfigure.AutoConfigurations;
22+
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
23+
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
24+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
25+
import org.springframework.jdbc.core.JdbcOperations;
26+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
27+
import org.springframework.jdbc.core.simple.JdbcClient;
28+
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
import static org.mockito.Mockito.mock;
31+
32+
/**
33+
* Tests for {@link JdbcClientAutoConfiguration}.
34+
*
35+
* @author Stephane Nicoll
36+
*/
37+
class JdbcClientAutoConfigurationTests {
38+
39+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
40+
.withPropertyValues("spring.datasource.generate-unique-name=true")
41+
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class, JdbcTemplateAutoConfiguration.class,
42+
JdbcClientAutoConfiguration.class));
43+
44+
@Test
45+
void jdbcClientWhenNoAvailableJdbcTemplateIsNotCreated() {
46+
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
47+
.run((context) -> assertThat(context).doesNotHaveBean(JdbcClient.class));
48+
}
49+
50+
@Test
51+
void jdbcClientWhenExistingJdbcTemplateIsCreated() {
52+
this.contextRunner.run((context) -> {
53+
assertThat(context).hasSingleBean(JdbcClient.class);
54+
NamedParameterJdbcTemplate namedParameterJdbcTemplate = context.getBean(NamedParameterJdbcTemplate.class);
55+
assertThat(namedParameterJdbcTemplate.getJdbcOperations()).isEqualTo(context.getBean(JdbcOperations.class));
56+
});
57+
}
58+
59+
@Test
60+
void jdbcClientWithCustomJdbcClientIsNotCreated() {
61+
this.contextRunner.withBean("customJdbcClient", JdbcClient.class, () -> mock(JdbcClient.class))
62+
.run((context) -> {
63+
assertThat(context).hasSingleBean(JdbcClient.class);
64+
assertThat(context.getBean(JdbcClient.class)).isEqualTo(context.getBean("customJdbcClient"));
65+
});
66+
}
67+
68+
@Test
69+
void jdbcClientIsOrderedAfterFlywayMigration() {
70+
this.contextRunner.withUserConfiguration(JdbcClientDataSourceMigrationValidator.class)
71+
.withPropertyValues("spring.flyway.locations:classpath:db/city")
72+
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
73+
.run((context) -> {
74+
assertThat(context).hasNotFailed().hasSingleBean(JdbcClient.class);
75+
assertThat(context.getBean(JdbcClientDataSourceMigrationValidator.class).count).isZero();
76+
});
77+
}
78+
79+
@Test
80+
void jdbcClientIsOrderedAfterLiquibaseMigration() {
81+
this.contextRunner.withUserConfiguration(JdbcClientDataSourceMigrationValidator.class)
82+
.withPropertyValues("spring.liquibase.changeLog:classpath:db/changelog/db.changelog-city.yaml")
83+
.withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class))
84+
.run((context) -> {
85+
assertThat(context).hasNotFailed().hasSingleBean(JdbcClient.class);
86+
assertThat(context.getBean(JdbcClientDataSourceMigrationValidator.class).count).isZero();
87+
});
88+
}
89+
90+
static class JdbcClientDataSourceMigrationValidator {
91+
92+
private final Integer count;
93+
94+
JdbcClientDataSourceMigrationValidator(JdbcClient jdbcClient) {
95+
this.count = jdbcClient.sql("SELECT COUNT(*) from CITY").query().singleValue(Integer.class);
96+
}
97+
98+
}
99+
100+
}

spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/sql.adoc

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[[data.sql]]
22
== SQL Databases
3-
The {spring-framework}[Spring Framework] provides extensive support for working with SQL databases, from direct JDBC access using `JdbcTemplate` to complete "`object relational mapping`" technologies such as Hibernate.
3+
The {spring-framework}[Spring Framework] provides extensive support for working with SQL databases, from direct JDBC access using `JdbcClient` or `JdbcTemplate` to complete "`object relational mapping`" technologies such as Hibernate.
44
{spring-data}[Spring Data] provides an additional level of functionality: creating `Repository` implementations directly from interfaces and using conventions to generate queries from your method names.
55

66

@@ -176,6 +176,17 @@ If more than one `JdbcTemplate` is defined and no primary candidate exists, the
176176

177177

178178

179+
[[data.sql.jdbc-client]]
180+
=== Using JdbcClient
181+
Spring's `JdbcClient` is auto-configured based on the presence of a `NamedParameterJdbcTemplate`.
182+
You can inject it directly in your own beans as well, as shown in the following example:
183+
184+
include::code:MyBean[]
185+
186+
If you rely on auto-configuration to create the underlying `JdbcTemplate`, any customization using `spring.jdbc.template.*` properties are taken into account in the client as well.
187+
188+
189+
179190
[[data.sql.jpa-and-spring-data]]
180191
=== JPA and Spring Data JPA
181192
The Java Persistence API is a standard technology that lets you "`map`" objects to relational databases.

spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/data-initialization.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ Spring Boot will automatically detect beans of the following types that depends
217217
- `AbstractEntityManagerFactoryBean` (unless configprop:spring.jpa.defer-datasource-initialization[] is set to `true`)
218218
- `DSLContext` (jOOQ)
219219
- `EntityManagerFactory` (unless configprop:spring.jpa.defer-datasource-initialization[] is set to `true`)
220+
- `JdbcClient`
220221
- `JdbcOperations`
221222
- `NamedParameterJdbcOperations`
222223

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.data.sql.jdbcclient;
18+
19+
import org.springframework.jdbc.core.simple.JdbcClient;
20+
import org.springframework.stereotype.Component;
21+
22+
@Component
23+
public class MyBean {
24+
25+
private final JdbcClient jdbcClient;
26+
27+
public MyBean(JdbcClient jdbcClient) {
28+
this.jdbcClient = jdbcClient;
29+
}
30+
31+
public void doSomething() {
32+
/* @chomp:line this.jdbcClient ... */ this.jdbcClient.sql("delete from customer").update();
33+
}
34+
35+
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/SpringJdbcDependsOnDatabaseInitializationDetector.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,14 +16,13 @@
1616

1717
package org.springframework.boot.jdbc;
1818

19-
import java.util.Arrays;
20-
import java.util.HashSet;
2119
import java.util.Set;
2220

2321
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDependsOnDatabaseInitializationDetector;
2422
import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector;
2523
import org.springframework.jdbc.core.JdbcOperations;
2624
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
25+
import org.springframework.jdbc.core.simple.JdbcClient;
2726

2827
/**
2928
* {@link DependsOnDatabaseInitializationDetector} for Spring Framework's JDBC support.
@@ -35,7 +34,7 @@ class SpringJdbcDependsOnDatabaseInitializationDetector
3534

3635
@Override
3736
protected Set<Class<?>> getDependsOnDatabaseInitializationBeanTypes() {
38-
return new HashSet<>(Arrays.asList(JdbcOperations.class, NamedParameterJdbcOperations.class));
37+
return Set.of(JdbcClient.class, JdbcOperations.class, NamedParameterJdbcOperations.class);
3938
}
4039

4140
}

0 commit comments

Comments
 (0)