From 1617485491c7019edbd949cd397af050bf5c3ac3 Mon Sep 17 00:00:00 2001
From: Ralf Ueberfuhr <40685729+ueberfuhr@users.noreply.github.com>
Date: Fri, 4 Feb 2022 17:58:16 +0100
Subject: [PATCH] BAEL-5323: Spring Boot with multiple data sources (#11741)
* BAEL-5323: Spring Boot with multiple data sources
* BAEL-5323: small fixes
* BAEL-5323: fix test class
* BAEL-5323: Refactor configuration classes
* BAEL-5323: Set proeprty to private
* BAEL-5323: rename test class to follow naming conventions
---
spring-boot-modules/pom.xml | 1 +
.../.gitignore | 1 +
.../spring-boot-multiple-datasources/pom.xml | 58 +++++++++++++++++++
.../MultipleDatasourcesApplication.java | 13 +++++
.../spring/datasources/todos/Todo.java | 48 +++++++++++++++
.../todos/TodoDatasourceConfiguration.java | 28 +++++++++
.../todos/TodoJpaConfiguration.java | 40 +++++++++++++
.../datasources/todos/TodoRepository.java | 6 ++
.../spring/datasources/topics/Topic.java | 39 +++++++++++++
.../topics/TopicDatasourceConfiguration.java | 26 +++++++++
.../topics/TopicJpaConfiguration.java | 41 +++++++++++++
.../datasources/topics/TopicRepository.java | 6 ++
.../src/main/resources/application.yml | 23 ++++++++
.../MultipleDatasourcesIntegrationTest.java | 39 +++++++++++++
14 files changed, 369 insertions(+)
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/.gitignore
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/pom.xml
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/MultipleDatasourcesApplication.java
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/Todo.java
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoDatasourceConfiguration.java
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoJpaConfiguration.java
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoRepository.java
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/Topic.java
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicDatasourceConfiguration.java
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicJpaConfiguration.java
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicRepository.java
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/main/resources/application.yml
create mode 100644 spring-boot-modules/spring-boot-multiple-datasources/src/test/java/com/baeldung/spring/datasources/MultipleDatasourcesIntegrationTest.java
diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml
index ac0deaa9e5..4925530a35 100644
--- a/spring-boot-modules/pom.xml
+++ b/spring-boot-modules/pom.xml
@@ -52,6 +52,7 @@
spring-boot-libraries
spring-boot-libraries-2
spring-boot-logging-log4j2
+ spring-boot-multiple-datasources
spring-boot-mvc
spring-boot-mvc-2
spring-boot-mvc-3
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/.gitignore b/spring-boot-modules/spring-boot-multiple-datasources/.gitignore
new file mode 100644
index 0000000000..87a3fce287
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/.gitignore
@@ -0,0 +1 @@
+.local-db
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/pom.xml b/spring-boot-modules/spring-boot-multiple-datasources/pom.xml
new file mode 100644
index 0000000000..d66095bc2c
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/pom.xml
@@ -0,0 +1,58 @@
+
+
+ 4.0.0
+ spring-boot-multiple-datasources
+ 0.1.0-SNAPSHOT
+ spring-boot-multiple-datasources
+ jar
+ Module For Spring Boot With Multiple Datasources
+
+
+ com.baeldung.spring-boot-modules
+ spring-boot-modules
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ com.h2database
+ h2
+ provided
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+ 2.6.3
+
+
+
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/MultipleDatasourcesApplication.java b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/MultipleDatasourcesApplication.java
new file mode 100644
index 0000000000..efdff387df
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/MultipleDatasourcesApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.spring.datasources;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class MultipleDatasourcesApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(MultipleDatasourcesApplication.class, args);
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/Todo.java b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/Todo.java
new file mode 100644
index 0000000000..6f8557e258
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/Todo.java
@@ -0,0 +1,48 @@
+package com.baeldung.spring.datasources.todos;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class Todo {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+ private String title;
+ private boolean completed;
+
+ public Todo() {
+ }
+
+ public Todo(String title) {
+ this.title = title;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public boolean isCompleted() {
+ return completed;
+ }
+
+ public void setCompleted(boolean completed) {
+ this.completed = completed;
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoDatasourceConfiguration.java b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoDatasourceConfiguration.java
new file mode 100644
index 0000000000..3bfc8e2855
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoDatasourceConfiguration.java
@@ -0,0 +1,28 @@
+package com.baeldung.spring.datasources.todos;
+
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import javax.sql.DataSource;
+
+@Configuration
+public class TodoDatasourceConfiguration {
+
+ @Bean
+ @ConfigurationProperties("spring.datasource.todos")
+ public DataSourceProperties todosDataSourceProperties() {
+ return new DataSourceProperties();
+ }
+
+ @Bean
+ @Primary
+ public DataSource todosDataSource() {
+ return todosDataSourceProperties()
+ .initializeDataSourceBuilder()
+ .build();
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoJpaConfiguration.java b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoJpaConfiguration.java
new file mode 100644
index 0000000000..655a3a55c2
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoJpaConfiguration.java
@@ -0,0 +1,40 @@
+package com.baeldung.spring.datasources.todos;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.util.Objects;
+
+@Configuration
+@EnableTransactionManagement
+@EnableJpaRepositories(
+ basePackageClasses = Todo.class,
+ entityManagerFactoryRef = "todosEntityManagerFactory",
+ transactionManagerRef = "todosTransactionManager"
+)
+public class TodoJpaConfiguration {
+
+ @Bean
+ public LocalContainerEntityManagerFactoryBean todosEntityManagerFactory(
+ @Qualifier("todosDataSource") DataSource dataSource,
+ EntityManagerFactoryBuilder builder) {
+ return builder
+ .dataSource(dataSource)
+ .packages(Todo.class)
+ .build();
+ }
+
+ @Bean
+ public PlatformTransactionManager todosTransactionManager(
+ @Qualifier("todosEntityManagerFactory") LocalContainerEntityManagerFactoryBean todosEntityManagerFactory) {
+ return new JpaTransactionManager(Objects.requireNonNull(todosEntityManagerFactory.getObject()));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoRepository.java b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoRepository.java
new file mode 100644
index 0000000000..09fb8c6500
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/todos/TodoRepository.java
@@ -0,0 +1,6 @@
+package com.baeldung.spring.datasources.todos;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface TodoRepository extends JpaRepository {
+}
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/Topic.java b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/Topic.java
new file mode 100644
index 0000000000..1d1f20f111
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/Topic.java
@@ -0,0 +1,39 @@
+package com.baeldung.spring.datasources.topics;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class Topic {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+ private String title;
+
+ public Topic() {
+ }
+
+ public Topic(String title) {
+ this.title = title;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicDatasourceConfiguration.java b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicDatasourceConfiguration.java
new file mode 100644
index 0000000000..a06983d681
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicDatasourceConfiguration.java
@@ -0,0 +1,26 @@
+package com.baeldung.spring.datasources.topics;
+
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+
+@Configuration
+public class TopicDatasourceConfiguration {
+
+ @Bean
+ @ConfigurationProperties("spring.datasource.topics")
+ public DataSourceProperties topicsDataSourceProperties() {
+ return new DataSourceProperties();
+ }
+
+ @Bean
+ public DataSource topicsDataSource() {
+ return topicsDataSourceProperties()
+ .initializeDataSourceBuilder()
+ .build();
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicJpaConfiguration.java b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicJpaConfiguration.java
new file mode 100644
index 0000000000..d800813b8c
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicJpaConfiguration.java
@@ -0,0 +1,41 @@
+package com.baeldung.spring.datasources.topics;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.util.Objects;
+
+@Configuration
+@EnableTransactionManagement
+@EnableJpaRepositories(
+ basePackageClasses = Topic.class,
+ entityManagerFactoryRef = "topicsEntityManagerFactory",
+ transactionManagerRef = "topicsTransactionManager"
+)
+public class TopicJpaConfiguration {
+
+ @Bean
+ public LocalContainerEntityManagerFactoryBean topicsEntityManagerFactory(
+ @Qualifier("topicsDataSource") DataSource dataSource,
+ EntityManagerFactoryBuilder builder
+ ) {
+ return builder
+ .dataSource(dataSource)
+ .packages(Topic.class)
+ .build();
+ }
+
+ @Bean
+ public PlatformTransactionManager topicsTransactionManager(
+ @Qualifier("topicsEntityManagerFactory") LocalContainerEntityManagerFactoryBean topicsEntityManagerFactory) {
+ return new JpaTransactionManager(Objects.requireNonNull(topicsEntityManagerFactory.getObject()));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicRepository.java b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicRepository.java
new file mode 100644
index 0000000000..499b72650a
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/java/com/baeldung/spring/datasources/topics/TopicRepository.java
@@ -0,0 +1,6 @@
+package com.baeldung.spring.datasources.topics;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface TopicRepository extends JpaRepository {
+}
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/main/resources/application.yml b/spring-boot-modules/spring-boot-multiple-datasources/src/main/resources/application.yml
new file mode 100644
index 0000000000..4754acfc0f
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/main/resources/application.yml
@@ -0,0 +1,23 @@
+spring:
+ datasource:
+ todos:
+ url: jdbc:h2:./.local-db/todos;DB_CLOSE_DELAY=-1;MODE=DB2;AUTO_SERVER=TRUE
+ username: sa
+ password: null
+ driverClassName: org.h2.Driver
+ topics:
+ url: jdbc:h2:./.local-db/topics;DB_CLOSE_DELAY=-1;MODE=DB2;AUTO_SERVER=TRUE
+ username: sa
+ password: null
+ driverClassName: org.h2.Driver
+ h2:
+ console:
+ enabled: true
+ path: /h2-console
+ jpa:
+ generate-ddl: true
+ hibernate:
+ ddl-auto: update
+ properties:
+ hibernate:
+ dialect: org.hibernate.dialect.H2Dialect
diff --git a/spring-boot-modules/spring-boot-multiple-datasources/src/test/java/com/baeldung/spring/datasources/MultipleDatasourcesIntegrationTest.java b/spring-boot-modules/spring-boot-multiple-datasources/src/test/java/com/baeldung/spring/datasources/MultipleDatasourcesIntegrationTest.java
new file mode 100644
index 0000000000..397992f6c8
--- /dev/null
+++ b/spring-boot-modules/spring-boot-multiple-datasources/src/test/java/com/baeldung/spring/datasources/MultipleDatasourcesIntegrationTest.java
@@ -0,0 +1,39 @@
+package com.baeldung.spring.datasources;
+
+import com.baeldung.spring.datasources.todos.Todo;
+import com.baeldung.spring.datasources.todos.TodoRepository;
+import com.baeldung.spring.datasources.topics.Topic;
+import com.baeldung.spring.datasources.topics.TopicRepository;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@DataJpaTest // no test database!
+class MultipleDatasourcesIntegrationTest {
+
+ @Autowired
+ TodoRepository todoRepo;
+ @Autowired
+ TopicRepository topicRepo;
+
+ @Test
+ void shouldSaveTodoToTodoDB() {
+ Todo todo = new Todo("test");
+ Todo saved =todoRepo.save(todo);
+ Optional result= todoRepo.findById(saved.getId());
+ assertThat(result).isPresent();
+ }
+
+ @Test
+ void shouldSaveTopicToTopicDB() {
+ Topic todo = new Topic("test");
+ Topic saved =topicRepo.save(todo);
+ Optional result= topicRepo.findById(saved.getId());
+ assertThat(result).isPresent();
+ }
+
+}