Skip to content

Commit 4efa459

Browse files
committed
Review doc on advanced datasource customization
Closes gh-7652
1 parent 4760a1a commit 4efa459

12 files changed

+774
-52
lines changed

spring-boot-docs/src/main/asciidoc/howto.adoc

Lines changed: 128 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,99 +1690,175 @@ a| `log4j2.json` +
16901690

16911691

16921692
[[howto-configure-a-datasource]]
1693-
=== Configure a DataSource
1694-
To override the default settings just define a `@Bean` of your own of type `DataSource`.
1695-
As explained in
1696-
<<spring-boot-features.adoc#boot-features-external-config-3rd-party-configuration>> you
1697-
can easily bind it to a set of `Environment` properties:
1693+
=== Configure a custom DataSource
1694+
To configure your own `DataSource` define a `@Bean` of that type in your configuration.
1695+
Spring Boot will reuse your `DataSource` anywhere one is required, including database
1696+
initialization. If you need to externalize some settings, you can easily bind your
1697+
`DataSource` to the environment (see
1698+
<<spring-boot-features.adoc#boot-features-external-config-3rd-party-configuration>>).
16981699

16991700
[source,java,indent=0,subs="verbatim,quotes,attributes"]
17001701
----
17011702
@Bean
1702-
@ConfigurationProperties(prefix="datasource.fancy")
1703+
@ConfigurationProperties(prefix="app.datasource")
17031704
public DataSource dataSource() {
17041705
return new FancyDataSource();
17051706
}
17061707
----
17071708

17081709
[source,properties,indent=0]
17091710
----
1710-
datasource.fancy.jdbcUrl=jdbc:h2:mem:mydb
1711-
datasource.fancy.username=sa
1712-
datasource.fancy.poolSize=30
1711+
app.datasource.url=jdbc:h2:mem:mydb
1712+
app.datasource.username=sa
1713+
app.datasource.pool-size=30
17131714
----
17141715

1715-
Spring Boot also provides a utility builder class `DataSourceBuilder` that can be used
1716-
to create one of the standard data sources (if it is on the classpath), or you can just
1717-
create your own. If you want to reuse the customizations of `DataSourceProperties`, you
1718-
can easily initialize a `DataSourceBuilder` from it:
1716+
Assuming that your `FancyDataSource` has regular JavaBean properties for the url, the
1717+
username and the pool size, these settings will be bound automatically before the
1718+
`DataSource` is made available to other components. The regular
1719+
<<howto-initialize-a-database-using-spring-jdbc,database initialization>> will also happen
1720+
(so the relevant sub-set of `spring.datasource.*` can still be used with your custom
1721+
configuration).
1722+
1723+
You can apply the same principle if you are configuring a custom JNDI `DataSource`:
17191724

17201725
[source,java,indent=0,subs="verbatim,quotes,attributes"]
17211726
----
1722-
@Bean
1723-
@ConfigurationProperties(prefix="datasource.mine")
1724-
public DataSource dataSource(DataSourceProperties properties) {
1725-
return properties.initializeDataSourceBuilder()
1726-
// additional customizations
1727-
.build();
1727+
@Bean(destroyMethod="")
1728+
@ConfigurationProperties(prefix="app.datasource")
1729+
public DataSource dataSource() throws Exception {
1730+
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
1731+
return dataSourceLookup.getDataSource("java:comp/env/jdbc/YourDS");
17281732
}
17291733
----
17301734

1735+
1736+
Spring Boot also provides a utility builder class `DataSourceBuilder` that can be used
1737+
to create one of the standard data sources (if it is on the classpath). The builder can
1738+
detect the one to use based on the ones available on the classpath and it also auto
1739+
detects the driver based on the JDBC url.
1740+
1741+
[source,java,indent=0,subs="verbatim,quotes,attributes"]
1742+
----
1743+
include::{code-examples}/jdbc/BasicDataSourceExample.java[tag=configuration]
1744+
----
1745+
1746+
To run an app with that `DataSource`, all that is needed really is the connection
1747+
information; pool-specific settings can also be provided, check the implementation that
1748+
is going to be used at runtime for more details.
1749+
1750+
[source,properties,indent=0]
1751+
----
1752+
app.datasource.url=jdbc:mysql://localhost/test
1753+
app.datasource.username=dbuser
1754+
app.datasource.password=dbpass
1755+
app.datasource.pool-size=30
1756+
----
1757+
1758+
There is a catch however. Because the actual type of the connection pool is not exposed,
1759+
no keys are generated in the metadata for your custom `DataSource` and no completion is
1760+
available in your IDE (The `DataSource` interface doesn't expose any property). Also, if
1761+
you happen to _only_ have Hikari on the classpath, this basic setup will not work because
1762+
Hikari has no `url` parameter (but a `jdbcUrl` parameter). You should have to rewrite
1763+
your configuration as follows:
1764+
1765+
[source,properties,indent=0]
1766+
----
1767+
app.datasource.jdbc-url=jdbc:mysql://localhost/test
1768+
app.datasource.username=dbuser
1769+
app.datasource.password=dbpass
1770+
app.datasource.maximum-pool-size=30
1771+
----
1772+
1773+
You can fix that by forcing the connection pool to use and return a dedicated
1774+
implementation rather than `DataSource`. You won't be able to change the implementation
1775+
at runtime but the list of options will be explicit.
1776+
1777+
[source,java,indent=0,subs="verbatim,quotes,attributes"]
1778+
----
1779+
include::{code-examples}/jdbc/SimpleDataSourceExample.java[tag=configuration]
1780+
----
1781+
1782+
You can even go further by leveraging what `DataSourceProperties` does for you, that is
1783+
providing a default embedded database if no url is provided with a sensible username and
1784+
password for it. You can easily initialize a `DataSourceBuilder` from the state of any
1785+
`DataSourceProperties` so you could just as well inject the one Spring Boot creates
1786+
automatically. However, that would split your configuration in two namespaces: url,
1787+
username, password, type and driver on `spring.datasource` and the rest on your custom
1788+
namespace (`app.datasource`). To avoid that, you can redefine a custom
1789+
`DataSourceProperties` on your custom namespace:
1790+
1791+
[source,java,indent=0,subs="verbatim,quotes,attributes"]
1792+
----
1793+
include::{code-examples}/jdbc/ConfigurableDataSourceExample.java[tag=configuration]
1794+
----
1795+
1796+
This setup puts you _in pair_ with what Spring Boot does for you by default, except that
1797+
a dedicated connection pool is chosen (in code) and its settings are exposed in the same
1798+
namespace. Because `DataSourceProperties` is taking care of the `url`/`jdbcUrl`
1799+
translation for you, you can configure it like this:
1800+
17311801
[source,properties,indent=0]
17321802
----
1733-
spring.datasource.url=jdbc:h2:mem:mydb
1734-
spring.datasource.username=sa
1735-
datasource.mine.poolSize=30
1803+
app.datasource.url=jdbc:mysql://localhost/test
1804+
app.datasource.username=dbuser
1805+
app.datasource.password=dbpass
1806+
app.datasource.maximum-pool-size=30
17361807
----
17371808

1738-
In this scenario, you keep the standard properties exposed by Spring Boot with your
1739-
custom `DataSource` arrangement. By adding `@ConfigurationProperties`, you can also
1740-
expose additional implementation-specific settings in a dedicated namespace.
1809+
NOTE: Because your custom configuration chooses to go with Hikari, `app.datasource.type`
1810+
will have no effect. In practice the builder will be initialized with whatever value you
1811+
might set there and then overridden by the call to `.type()``.
17411812

17421813
See _<<spring-boot-features.adoc#boot-features-configure-datasource>>_ in the
17431814
'`Spring Boot features`' section and the
17441815
{sc-spring-boot-autoconfigure}/jdbc/DataSourceAutoConfiguration.{sc-ext}[`DataSourceAutoConfiguration`]
17451816
class for more details.
17461817

1747-
[TIP]
1748-
====
1749-
You could also do that if you want to configure a JNDI data-source.
1818+
1819+
1820+
[[howto-two-datasources]]
1821+
=== Configure Two DataSources
1822+
If you need to configure multiple data sources, you can apply the same tricks that are
1823+
described in the previous section. You must, however, mark one of the `DataSource`
1824+
`@Primary` as various auto-configurations down the road expect to be able to get one by
1825+
type.
1826+
1827+
If you create your own `DataSource`, the auto-configuration will back off. In the example
1828+
below, we provide the _exact_ same features set than what the auto-configuration provides
1829+
on the primary data source:
17501830

17511831
[source,java,indent=0,subs="verbatim,quotes,attributes"]
17521832
----
1753-
@Bean(destroyMethod="")
1754-
@ConfigurationProperties(prefix="datasource.mine")
1755-
public DataSource dataSource() throws Exception {
1756-
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
1757-
return dataSourceLookup.getDataSource("java:comp/env/jdbc/YourDS");
1758-
}
1833+
include::{code-examples}/jdbc/SimpleTwoDataSourcesExample.java[tag=configuration]
17591834
----
1760-
====
17611835

1836+
TIP: `fooDataSourceProperties` has to be flagged `@Primary` so that the database
1837+
initializer feature uses your copy (should you use that).
17621838

1839+
Both data sources are also bound for advanced customizations. For instance you could
1840+
configure them as follows:
17631841

1764-
[[howto-two-datasources]]
1765-
=== Configure Two DataSources
1766-
Creating more than one data source works the same as creating the first one. You might
1767-
want to mark one of them as `@Primary` if you are using the default auto-configuration for
1768-
JDBC or JPA (then that one will be picked up by any `@Autowired` injections).
1842+
[source,properties,indent=0]
1843+
----
1844+
app.datasource.foo.type=com.zaxxer.hikari.HikariDataSource
1845+
app.datasource.foo.maximum-pool-size=30
17691846
1770-
[source,java,indent=0,subs="verbatim,quotes,attributes"]
1847+
app.datasource.bar.url=jdbc:mysql://localhost/test
1848+
app.datasource.bar.username=dbuser
1849+
app.datasource.bar.password=dbpass
1850+
app.datasource.bar.max-total=30
17711851
----
1772-
@Bean
1773-
@Primary
1774-
@ConfigurationProperties(prefix="datasource.primary")
1775-
public DataSource primaryDataSource() {
1776-
return DataSourceBuilder.create().build();
1777-
}
17781852

1779-
@Bean
1780-
@ConfigurationProperties(prefix="datasource.secondary")
1781-
public DataSource secondaryDataSource() {
1782-
return DataSourceBuilder.create().build();
1783-
}
1853+
Of course, you can apply the same concept to the secondary `DataSource` as well:
1854+
1855+
[source,java,indent=0,subs="verbatim,quotes,attributes"]
1856+
----
1857+
include::{code-examples}/jdbc/CompleteTwoDataSourcesExample.java[tag=configuration]
17841858
----
17851859

1860+
This final example configures two data sources on custom namespaces with the same logic
1861+
than what Spring Boot would do in auto-configuration.
17861862

17871863

17881864
[[howto-use-spring-data-repositories]]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2012-2017 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+
* http://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.jdbc;
18+
19+
import javax.sql.DataSource;
20+
21+
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
22+
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
import org.springframework.context.annotation.Bean;
24+
import org.springframework.context.annotation.Configuration;
25+
26+
/**
27+
* Example configuration for configuring a very basic custom {@link DataSource}.
28+
*
29+
* @author Stephane Nicoll
30+
*/
31+
public class BasicDataSourceExample {
32+
33+
/**
34+
* A configuration that exposes an empty {@link DataSource}.
35+
*/
36+
@Configuration
37+
static class BasicDataSourceConfiguration {
38+
39+
// tag::configuration[]
40+
@Bean
41+
@ConfigurationProperties("app.datasource")
42+
public DataSource dataSource() {
43+
return DataSourceBuilder.create().build();
44+
}
45+
// end::configuration[]
46+
47+
}
48+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2012-2017 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+
* http://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.jdbc;
18+
19+
import javax.sql.DataSource;
20+
21+
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
22+
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
import org.springframework.context.annotation.Bean;
24+
import org.springframework.context.annotation.Configuration;
25+
import org.springframework.context.annotation.Primary;
26+
27+
/**
28+
* Example configuration for configuring two data sources with what Spring Boot does
29+
* in auto-configuration.
30+
*
31+
* @author Stephane Nicoll
32+
*/
33+
public class CompleteTwoDataSourcesExample {
34+
35+
/**
36+
* A complete configuration that exposes two data sources.
37+
*/
38+
@Configuration
39+
static class CompleteDataSourcesConfiguration {
40+
41+
// tag::configuration[]
42+
@Bean
43+
@Primary
44+
@ConfigurationProperties("app.datasource.foo")
45+
public DataSourceProperties fooDataSourceProperties() {
46+
return new DataSourceProperties();
47+
}
48+
49+
@Bean
50+
@Primary
51+
@ConfigurationProperties("app.datasource.foo")
52+
public DataSource fooDataSource() {
53+
return fooDataSourceProperties()
54+
.initializeDataSourceBuilder()
55+
.build();
56+
}
57+
58+
@Bean
59+
@ConfigurationProperties("app.datasource.bar")
60+
public DataSourceProperties barDataSourceProperties() {
61+
return new DataSourceProperties();
62+
}
63+
64+
65+
@Bean
66+
@ConfigurationProperties("app.datasource.bar")
67+
public DataSource barDataSource() {
68+
return barDataSourceProperties()
69+
.initializeDataSourceBuilder()
70+
.build();
71+
}
72+
// end::configuration[]
73+
74+
}
75+
76+
}

0 commit comments

Comments
 (0)