Skip to content

Commit 72183d6

Browse files
committed
Add multiple datasource how to for spring data jdbc
1 parent f58cc5a commit 72183d6

File tree

19 files changed

+452
-1
lines changed

19 files changed

+452
-1
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
== Spring Data JDBC How To use multiple DataSources.
2+
3+
Spring Data JDBC requires special configuration if multiple DataSources are to be present in the ApplicationContext.
4+
5+
Configuration is provided to setup two DataSources, each with their own schema and data files. Review the `DataSourceConfiguration` class for this configuration. Note that the first DataSource configuration utilizes Spring Data JDBC's automatic configuration, whereas the second DataSource explicitly disables the AutoConfiguration so that Beans can be manually created.
6+
7+
The Beans created in `DataSourceConfiguration`, notably, the `NamedParameterJdbcOperations` and `TransactionManager` instances are then referenced in the `@EnableJdbcRepositories` configuration. Specifically, you specify the `jdbcOperationsRef` and `transactionManagerRef`. Please review the `JdbcConfiguration` class.
8+
9+
A sample `Service` is provided to bring together two Spring Data JDBC Repositories that are backed by different DataSources. Review the `EvilEmpire` Component and corresponding `EvilEmpireTests` classes.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<artifactId>spring-data-jdbc-how-to-multiple-datasources</artifactId>
6+
7+
<parent>
8+
<groupId>org.springframework.data.examples</groupId>
9+
<artifactId>spring-data-jdbc-how-to</artifactId>
10+
<version>2.0.0.BUILD-SNAPSHOT</version>
11+
<relativePath>../pom.xml</relativePath>
12+
</parent>
13+
14+
<name>Spring Data JDBC - How to configure multiple datasources</name>
15+
<description>Sample project for Spring Data JDBC demonstrating how to configure multiple datasources.
16+
</description>
17+
<url>https://projects.spring.io/spring-data-jdbc</url>
18+
<inceptionYear>2025</inceptionYear>
19+
20+
<dependencies>
21+
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-data-jdbc</artifactId>
25+
</dependency>
26+
27+
<dependency>
28+
<groupId>com.h2database</groupId>
29+
<artifactId>h2</artifactId>
30+
<scope>runtime</scope>
31+
</dependency>
32+
33+
<dependency>
34+
<groupId>org.springframework.boot</groupId>
35+
<artifactId>spring-boot-starter-test</artifactId>
36+
<scope>test</scope>
37+
</dependency>
38+
39+
</dependencies>
40+
41+
<build>
42+
<plugins>
43+
<plugin>
44+
<groupId>org.springframework.boot</groupId>
45+
<artifactId>spring-boot-maven-plugin</artifactId>
46+
</plugin>
47+
</plugins>
48+
</build>
49+
50+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package example.springdata.jdbc.howto.multipledatasources;
2+
3+
import example.springdata.jdbc.howto.multipledatasources.minion.Minion;
4+
import example.springdata.jdbc.howto.multipledatasources.minion.MinionRepository;
5+
import example.springdata.jdbc.howto.multipledatasources.person.PersonRepository;
6+
import org.springframework.stereotype.Component;
7+
8+
import java.util.List;
9+
10+
@Component
11+
public class EvilEmpire {
12+
13+
private final PersonRepository personRepository;
14+
private final MinionRepository minionRepository;
15+
16+
public EvilEmpire( PersonRepository personRepository, MinionRepository minionRepository ) {
17+
18+
this.personRepository = personRepository;
19+
this.minionRepository = minionRepository;
20+
21+
}
22+
23+
public List<String> getMinionsByEvilMaster( Long evilMaster ) {
24+
25+
var person = this.personRepository.findById( evilMaster );
26+
if ( person.isPresent() ) {
27+
28+
return this.minionRepository.findByEvilMaster( person.get().id() ).stream()
29+
.map( Minion::name )
30+
.toList();
31+
32+
}
33+
34+
throw new IllegalArgumentException( "No Minions found for Evil Master [" + evilMaster + "]" );
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package example.springdata.jdbc.howto.multipledatasources;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
class MultipleDataSourceApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(MultipleDataSourceApplication.class, args);
11+
}
12+
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package example.springdata.jdbc.howto.multipledatasources.configuration;
2+
3+
import org.springframework.beans.factory.annotation.Qualifier;
4+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
5+
import org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration;
6+
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
import org.springframework.context.annotation.Lazy;
10+
import org.springframework.data.jdbc.core.convert.DefaultJdbcTypeFactory;
11+
import org.springframework.data.jdbc.core.convert.JdbcConverter;
12+
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
13+
import org.springframework.data.jdbc.core.convert.MappingJdbcConverter;
14+
import org.springframework.data.jdbc.core.convert.RelationResolver;
15+
import org.springframework.data.jdbc.core.dialect.DialectResolver;
16+
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
17+
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
18+
import org.springframework.data.relational.core.mapping.DefaultNamingStrategy;
19+
import org.springframework.data.relational.core.mapping.NamingStrategy;
20+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
21+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
22+
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
23+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
24+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
25+
import org.springframework.transaction.PlatformTransactionManager;
26+
27+
import javax.sql.DataSource;
28+
import java.util.Optional;
29+
30+
@Configuration
31+
public class DataSourceConfiguration {
32+
33+
@Configuration
34+
static class Ds1Configuration {
35+
36+
@Bean("ds1")
37+
DataSource dataSource1() {
38+
39+
var builder = new EmbeddedDatabaseBuilder();
40+
41+
return builder
42+
.setType( EmbeddedDatabaseType.H2 )
43+
.generateUniqueName( true )
44+
.addScript( "classpath:/ds1-schema.sql" )
45+
.addScript( "classpath:/ds1-data.sql" )
46+
.build();
47+
}
48+
49+
@Bean("jdbcOperations1" )
50+
NamedParameterJdbcOperations jdbcOperations1() {
51+
52+
return new NamedParameterJdbcTemplate( dataSource1() );
53+
}
54+
55+
@Bean( "transactionManager1" )
56+
PlatformTransactionManager transactionManager1() {
57+
58+
return new DataSourceTransactionManager( dataSource1() );
59+
}
60+
61+
}
62+
63+
@Configuration
64+
@EnableAutoConfiguration(exclude = {
65+
DataSourceAutoConfiguration.class,
66+
JdbcRepositoriesAutoConfiguration.class
67+
})
68+
static class Ds2Configuration {
69+
70+
@Bean( "ds2" )
71+
DataSource dataSource2() {
72+
73+
var builder = new EmbeddedDatabaseBuilder();
74+
75+
return builder
76+
.setType( EmbeddedDatabaseType.H2 )
77+
.generateUniqueName( true )
78+
.addScript( "classpath:/ds2-schema.sql" )
79+
.addScript( "classpath:/ds2-data.sql" )
80+
.build();
81+
}
82+
83+
@Bean( "jdbcOperations2" )
84+
NamedParameterJdbcOperations jdbcOperations1() {
85+
86+
return new NamedParameterJdbcTemplate( dataSource2() );
87+
}
88+
89+
@Bean( "transactionManager2" )
90+
PlatformTransactionManager transactionManager2() {
91+
92+
return new DataSourceTransactionManager( dataSource2() );
93+
}
94+
95+
@Bean
96+
JdbcDialect jdbcDialect2( @Qualifier( "jdbcOperations2" ) NamedParameterJdbcOperations jdbcOperations ) {
97+
98+
return DialectResolver.getDialect( jdbcOperations.getJdbcOperations() );
99+
}
100+
101+
@Bean
102+
JdbcCustomConversions customConversions2() {
103+
104+
return new JdbcCustomConversions();
105+
}
106+
107+
@Bean
108+
JdbcMappingContext jdbcMappingContext2( Optional<NamingStrategy> namingStrategy, JdbcCustomConversions customConversions2 ) {
109+
110+
var mappingContext = new JdbcMappingContext( namingStrategy.orElse( DefaultNamingStrategy.INSTANCE ) );
111+
mappingContext.setSimpleTypeHolder( customConversions2.getSimpleTypeHolder() );
112+
113+
return mappingContext;
114+
}
115+
116+
@Bean
117+
JdbcConverter jdbcConverter2(
118+
JdbcMappingContext jdbcMappingContext2,
119+
@Qualifier( "jdbcOperations2" ) NamedParameterJdbcOperations jdbcOperations2,
120+
@Lazy RelationResolver relationResolver,
121+
JdbcCustomConversions customConversions2
122+
) {
123+
124+
var jdbcTypeFactory = new DefaultJdbcTypeFactory( jdbcOperations2.getJdbcOperations() );
125+
126+
return new MappingJdbcConverter( jdbcMappingContext2, relationResolver, customConversions2, jdbcTypeFactory );
127+
}
128+
129+
}
130+
131+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package example.springdata.jdbc.howto.multipledatasources.configuration;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
5+
6+
@Configuration
7+
public class JdbcConfiguration {
8+
9+
@Configuration
10+
@EnableJdbcRepositories(
11+
basePackages = { "example.springdata.jdbc.howto.multipledatasources.minion" },
12+
jdbcOperationsRef = "jdbcOperations1",
13+
transactionManagerRef = "transactionManager1"
14+
)
15+
static class MinionJdbcConfiguration {
16+
17+
}
18+
19+
@Configuration
20+
@EnableJdbcRepositories(
21+
basePackages = { "example.springdata.jdbc.howto.multipledatasources.person" },
22+
jdbcOperationsRef = "jdbcOperations2",
23+
transactionManagerRef = "transactionManager2"
24+
)
25+
static class PersonJdbcConfiguration {
26+
27+
}
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2021 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+
package example.springdata.jdbc.howto.multipledatasources.minion;
17+
18+
import org.springframework.data.annotation.Id;
19+
20+
public record Minion(
21+
22+
@Id
23+
Long id,
24+
25+
String name,
26+
27+
Long evilMaster
28+
29+
) { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2021 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+
package example.springdata.jdbc.howto.multipledatasources.minion;
17+
18+
import org.springframework.data.jdbc.repository.query.Query;
19+
import org.springframework.data.repository.CrudRepository;
20+
import org.springframework.data.repository.query.Param;
21+
22+
import java.util.Collection;
23+
24+
public interface MinionRepository extends CrudRepository<Minion, Long> {
25+
26+
@Query( "SELECT * FROM MINION WHERE EVIL_MASTER = :id" )
27+
Collection<Minion> findByEvilMaster( @Param( "id" ) Long id );
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2021 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+
package example.springdata.jdbc.howto.multipledatasources.person;
17+
18+
import org.springframework.data.annotation.Id;
19+
20+
public record Person(
21+
22+
@Id
23+
Long id,
24+
25+
String name
26+
27+
) { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2021 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+
package example.springdata.jdbc.howto.multipledatasources.person;
17+
18+
import org.springframework.data.repository.CrudRepository;
19+
20+
public interface PersonRepository extends CrudRepository<Person, Long> {
21+
22+
}

0 commit comments

Comments
 (0)