Skip to content

Add Spring Data JDBC How To for Multiple DataSources #699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions jdbc/howto/multiple-datasources/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
== Spring Data JDBC How To use multiple DataSources.

Spring Data JDBC requires special configuration if multiple DataSources are to be present in the ApplicationContext.

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.

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.

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.
50 changes: 50 additions & 0 deletions jdbc/howto/multiple-datasources/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-data-jdbc-how-to-multiple-datasources</artifactId>

<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-jdbc-how-to</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<name>Spring Data JDBC - How to configure multiple datasources</name>
<description>Sample project for Spring Data JDBC demonstrating how to configure multiple datasources.
</description>
<url>https://projects.spring.io/spring-data-jdbc</url>
<inceptionYear>2025</inceptionYear>

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jdbc.howto.multipledatasources;

import example.springdata.jdbc.howto.multipledatasources.minion.Minion;
import example.springdata.jdbc.howto.multipledatasources.minion.MinionRepository;
import example.springdata.jdbc.howto.multipledatasources.person.PersonRepository;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* Sample `Service` that uses both Spring Data JDBC Repositories
*
* @author Daniel Frey
*/
@Component
public class EvilEmpire {

private final PersonRepository personRepository;
private final MinionRepository minionRepository;

public EvilEmpire( PersonRepository personRepository, MinionRepository minionRepository ) {

this.personRepository = personRepository;
this.minionRepository = minionRepository;

}

public List<String> getMinionsByEvilMaster( Long evilMaster ) {

var person = this.personRepository.findById( evilMaster );
if ( person.isPresent() ) {

return this.minionRepository.findByEvilMaster( person.get().id() ).stream()
.map( Minion::name )
.toList();

}

throw new IllegalArgumentException( "No Minions found for Evil Master [" + evilMaster + "]" );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jdbc.howto.multipledatasources;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* @author Daniel Frey
*/
@SpringBootApplication
class MultipleDataSourceApplication {

public static void main(String[] args) {
SpringApplication.run(MultipleDataSourceApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jdbc.howto.multipledatasources.configuration;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.jdbc.core.convert.DefaultJdbcTypeFactory;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.core.convert.MappingJdbcConverter;
import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.jdbc.core.dialect.DialectResolver;
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.relational.core.mapping.DefaultNamingStrategy;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.Optional;

/**
* Multiple DataSource Configuration
*
* Sets up <code>DataSource</code>, <codd>NamedParameterJdbcOperations</codd>, and <code>TransactionManager</code>
* for each Spring Data JDBC Repository.
*
* The second configuration also sets up the required components for Spring Data JDBC without using explicit
* AutoConfiguration.
*
* @author Daniel Frey
*/
@Configuration
public class DataSourceConfiguration {

@Configuration
static class Ds1Configuration {

@Bean("ds1")
DataSource dataSource1() {

var builder = new EmbeddedDatabaseBuilder();

return builder
.setType( EmbeddedDatabaseType.H2 )
.generateUniqueName( true )
.addScript( "classpath:/ds1-schema.sql" )
.addScript( "classpath:/ds1-data.sql" )
.build();
}

@Bean("jdbcOperations1" )
NamedParameterJdbcOperations jdbcOperations1() {

return new NamedParameterJdbcTemplate( dataSource1() );
}

@Bean( "transactionManager1" )
PlatformTransactionManager transactionManager1() {

return new DataSourceTransactionManager( dataSource1() );
}

}

@Configuration
@EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
JdbcRepositoriesAutoConfiguration.class
})
static class Ds2Configuration {

@Bean( "ds2" )
DataSource dataSource2() {

var builder = new EmbeddedDatabaseBuilder();

return builder
.setType( EmbeddedDatabaseType.H2 )
.generateUniqueName( true )
.addScript( "classpath:/ds2-schema.sql" )
.addScript( "classpath:/ds2-data.sql" )
.build();
}

@Bean( "jdbcOperations2" )
NamedParameterJdbcOperations jdbcOperations1() {

return new NamedParameterJdbcTemplate( dataSource2() );
}

@Bean( "transactionManager2" )
PlatformTransactionManager transactionManager2() {

return new DataSourceTransactionManager( dataSource2() );
}

@Bean
JdbcDialect jdbcDialect2( @Qualifier( "jdbcOperations2" ) NamedParameterJdbcOperations jdbcOperations ) {

return DialectResolver.getDialect( jdbcOperations.getJdbcOperations() );
}

@Bean
JdbcCustomConversions customConversions2() {

return new JdbcCustomConversions();
}

@Bean
JdbcMappingContext jdbcMappingContext2( Optional<NamingStrategy> namingStrategy, JdbcCustomConversions customConversions2 ) {

var mappingContext = new JdbcMappingContext( namingStrategy.orElse( DefaultNamingStrategy.INSTANCE ) );
mappingContext.setSimpleTypeHolder( customConversions2.getSimpleTypeHolder() );

return mappingContext;
}

@Bean
JdbcConverter jdbcConverter2(
JdbcMappingContext jdbcMappingContext2,
@Qualifier( "jdbcOperations2" ) NamedParameterJdbcOperations jdbcOperations2,
@Lazy RelationResolver relationResolver,
JdbcCustomConversions customConversions2
) {

var jdbcTypeFactory = new DefaultJdbcTypeFactory( jdbcOperations2.getJdbcOperations() );

return new MappingJdbcConverter( jdbcMappingContext2, relationResolver, customConversions2, jdbcTypeFactory );
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jdbc.howto.multipledatasources.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;

/**
* Configures the Spring Data JDBC Repositories with explicit callouts to `jdbcOperationsRef`
* and `transactionManagerRef`
*
* @see <code>DataSourceConfiguration.java</code>
*
* @author Daniel Frey
*/
@Configuration
public class JdbcConfiguration {

@Configuration
@EnableJdbcRepositories(
basePackages = { "example.springdata.jdbc.howto.multipledatasources.minion" },
jdbcOperationsRef = "jdbcOperations1",
transactionManagerRef = "transactionManager1"
)
static class MinionJdbcConfiguration {

}

@Configuration
@EnableJdbcRepositories(
basePackages = { "example.springdata.jdbc.howto.multipledatasources.person" },
jdbcOperationsRef = "jdbcOperations2",
transactionManagerRef = "transactionManager2"
)
static class PersonJdbcConfiguration {

}

}
Loading