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(); + } + +}