diff --git a/persistence-modules/spring-boot-persistence/README.md b/persistence-modules/spring-boot-persistence/README.md index 5b9fbf7b79..a9fe3905c2 100644 --- a/persistence-modules/spring-boot-persistence/README.md +++ b/persistence-modules/spring-boot-persistence/README.md @@ -7,4 +7,5 @@ - [Resolving “Failed to Configure a DataSource” Error](https://www.baeldung.com/spring-boot-failed-to-configure-data-source) - [Hibernate Field Naming with Spring Boot](https://www.baeldung.com/hibernate-field-naming-spring-boot) - [Spring Boot with Hibernate](https://www.baeldung.com/spring-boot-hibernate) +- [A Guide to Spring AbstractRoutingDatasource](https://www.baeldung.com/spring-abstract-routing-data-source) - More articles: [[more -->]](../spring-boot-persistence-2) \ No newline at end of file diff --git a/persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientDao.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientDao.java similarity index 100% rename from persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientDao.java rename to persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientDao.java diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientDataSourceRouter.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientDataSourceRouter.java new file mode 100644 index 0000000000..ebedaf1045 --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientDataSourceRouter.java @@ -0,0 +1,27 @@ +package com.baeldung.dsrouting; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/** + * Returns thread bound client lookup key for current context. + */ +public class ClientDataSourceRouter extends AbstractRoutingDataSource { + + @Override + protected Object determineCurrentLookupKey() { + return ClientDatabaseContextHolder.getClientDatabase(); + } + + public void initDatasource(DataSource clientADataSource, + DataSource clientBDataSource) { + Map dataSourceMap = new HashMap<>(); + dataSourceMap.put(ClientDatabase.CLIENT_A, clientADataSource); + dataSourceMap.put(ClientDatabase.CLIENT_A, clientBDataSource); + this.setTargetDataSources(dataSourceMap); + this.setDefaultTargetDataSource(clientADataSource); + } +} diff --git a/persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientDatabase.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientDatabase.java similarity index 100% rename from persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientDatabase.java rename to persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientDatabase.java diff --git a/persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientDatabaseContextHolder.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientDatabaseContextHolder.java similarity index 100% rename from persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientDatabaseContextHolder.java rename to persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientDatabaseContextHolder.java diff --git a/persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientService.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientService.java similarity index 100% rename from persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientService.java rename to persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/ClientService.java diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/model/ClientADetails.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/model/ClientADetails.java new file mode 100644 index 0000000000..c7236fed3c --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/model/ClientADetails.java @@ -0,0 +1,28 @@ +package com.baeldung.dsrouting.model; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "client-a.datasource") +public class ClientADetails { + + private String name; + private String script; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } +} diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/model/ClientBDetails.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/model/ClientBDetails.java new file mode 100644 index 0000000000..5776c79855 --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/dsrouting/model/ClientBDetails.java @@ -0,0 +1,28 @@ +package com.baeldung.dsrouting.model; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "client-b.datasource") +public class ClientBDetails { + + private String name; + private String script; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } +} diff --git a/persistence-modules/spring-jpa/src/test/java/com/baeldung/dsrouting/DataSourceRoutingIntegrationTest.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/DataSourceRoutingIntegrationTest.java similarity index 100% rename from persistence-modules/spring-jpa/src/test/java/com/baeldung/dsrouting/DataSourceRoutingIntegrationTest.java rename to persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/DataSourceRoutingIntegrationTest.java diff --git a/persistence-modules/spring-jpa/src/test/java/com/baeldung/dsrouting/DataSourceRoutingTestConfiguration.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/DataSourceRoutingTestConfiguration.java similarity index 92% rename from persistence-modules/spring-jpa/src/test/java/com/baeldung/dsrouting/DataSourceRoutingTestConfiguration.java rename to persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/DataSourceRoutingTestConfiguration.java index cec9343892..957114eba5 100644 --- a/persistence-modules/spring-jpa/src/test/java/com/baeldung/dsrouting/DataSourceRoutingTestConfiguration.java +++ b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/DataSourceRoutingTestConfiguration.java @@ -34,11 +34,11 @@ public class DataSourceRoutingTestConfiguration { private DataSource clientADatasource() { EmbeddedDatabaseBuilder dbBuilder = new EmbeddedDatabaseBuilder(); - return dbBuilder.setType(EmbeddedDatabaseType.H2).setName("CLIENT_A").addScript("classpath:dsrouting-db.sql").build(); + return dbBuilder.setType(EmbeddedDatabaseType.H2).setName("CLIENT_A").addScript("dsrouting-db.sql").build(); } private DataSource clientBDatasource() { EmbeddedDatabaseBuilder dbBuilder = new EmbeddedDatabaseBuilder(); - return dbBuilder.setType(EmbeddedDatabaseType.H2).setName("CLIENT_B").addScript("classpath:dsrouting-db.sql").build(); + return dbBuilder.setType(EmbeddedDatabaseType.H2).setName("CLIENT_B").addScript("dsrouting-db.sql").build(); } } diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/SpringBootDataSourceRoutingIntegrationTest.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/SpringBootDataSourceRoutingIntegrationTest.java new file mode 100644 index 0000000000..75829c2153 --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/SpringBootDataSourceRoutingIntegrationTest.java @@ -0,0 +1,62 @@ +package com.baeldung.dsrouting; + +import static org.junit.Assert.assertEquals; + +import javax.sql.DataSource; + +import com.baeldung.dsrouting.model.ClientADetails; +import com.baeldung.dsrouting.model.ClientBDetails; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = {ClientADetails.class, ClientBDetails.class}) +@ContextConfiguration(classes = SpringBootDataSourceRoutingTestConfiguration.class) +@DirtiesContext +@EnableConfigurationProperties(ClientBDetails.class) +public class SpringBootDataSourceRoutingIntegrationTest { + + @Autowired + DataSource routingDatasource; + + @Autowired + ClientService clientService; + + @Before + public void setup() { + final String SQL_CLIENT_A = "insert into client (id, name) values (1, 'CLIENT A')"; + final String SQL_CLIENT_B = "insert into client (id, name) values (2, 'CLIENT B')"; + + JdbcTemplate jdbcTemplate = new JdbcTemplate(); + jdbcTemplate.setDataSource(routingDatasource); + + ClientDatabaseContextHolder.set(ClientDatabase.CLIENT_A); + jdbcTemplate.execute(SQL_CLIENT_A); + ClientDatabaseContextHolder.clear(); + + ClientDatabaseContextHolder.set(ClientDatabase.CLIENT_B); + jdbcTemplate.execute(SQL_CLIENT_B); + ClientDatabaseContextHolder.clear(); + } + + @Test + public void givenClientDbs_whenContextsSwitch_thenRouteToCorrectDatabase() throws Exception { + + // test ACME WIDGETS + String clientName = clientService.getClientName(ClientDatabase.CLIENT_A); + assertEquals(clientName, "CLIENT A"); + + // test WIDGETS_ARE_US + clientName = clientService.getClientName(ClientDatabase.CLIENT_B); + assertEquals(clientName, "CLIENT B"); + } +} diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/SpringBootDataSourceRoutingTestConfiguration.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/SpringBootDataSourceRoutingTestConfiguration.java new file mode 100644 index 0000000000..01f157998f --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/dsrouting/SpringBootDataSourceRoutingTestConfiguration.java @@ -0,0 +1,55 @@ +package com.baeldung.dsrouting; + +import com.baeldung.dsrouting.model.ClientADetails; +import com.baeldung.dsrouting.model.ClientBDetails; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.*; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class SpringBootDataSourceRoutingTestConfiguration { + @Autowired + private ClientADetails clientADetails; + @Autowired + private ClientBDetails clientBDetails; + + @Bean + public ClientService clientService() { + return new ClientService(new ClientDao(clientDatasource())); + } + + @Bean + public DataSource clientDatasource() { + Map targetDataSources = new HashMap<>(); + DataSource clientADatasource = clientADatasource(); + DataSource clientBDatasource = clientBDatasource(); + targetDataSources.put(ClientDatabase.CLIENT_A, clientADatasource); + targetDataSources.put(ClientDatabase.CLIENT_B, clientBDatasource); + + ClientDataSourceRouter clientRoutingDatasource = new ClientDataSourceRouter(); + clientRoutingDatasource.setTargetDataSources(targetDataSources); + clientRoutingDatasource.setDefaultTargetDataSource(clientADatasource); + return clientRoutingDatasource; + } + + private DataSource clientADatasource() { + EmbeddedDatabaseBuilder dbBuilder = new EmbeddedDatabaseBuilder(); + return dbBuilder.setType(EmbeddedDatabaseType.H2) + .setName(clientADetails.getName()) + .addScript(clientADetails.getScript()) + .build(); + } + + private DataSource clientBDatasource() { + EmbeddedDatabaseBuilder dbBuilder = new EmbeddedDatabaseBuilder(); + return dbBuilder.setType(EmbeddedDatabaseType.H2) + .setName(clientBDetails.getName()) + .addScript(clientBDetails.getScript()) + .build(); + } +} diff --git a/persistence-modules/spring-boot-persistence/src/test/resources/application.properties b/persistence-modules/spring-boot-persistence/src/test/resources/application.properties index d22bd38426..b268f46094 100644 --- a/persistence-modules/spring-boot-persistence/src/test/resources/application.properties +++ b/persistence-modules/spring-boot-persistence/src/test/resources/application.properties @@ -4,6 +4,14 @@ spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 spring.datasource.username=sa spring.datasource.password=sa +#database details for CLIENT_A +client-a.datasource.name=CLIENT_A +client-a.datasource.script=dsrouting-db.sql + +#database details for CLIENT_B +client-b.datasource.name=CLIENT_B +client-b.datasource.script=dsrouting-db.sql + # hibernate.X hibernate.dialect=org.hibernate.dialect.H2Dialect hibernate.show_sql=true diff --git a/persistence-modules/spring-boot-persistence/src/test/resources/dsrouting-db.sql b/persistence-modules/spring-boot-persistence/src/test/resources/dsrouting-db.sql new file mode 100644 index 0000000000..c9ca52907a --- /dev/null +++ b/persistence-modules/spring-boot-persistence/src/test/resources/dsrouting-db.sql @@ -0,0 +1,5 @@ +create table client ( + id numeric, + name varchar(50), + constraint pk_client primary key (id) +); \ No newline at end of file diff --git a/persistence-modules/spring-jpa/README.md b/persistence-modules/spring-jpa/README.md index db70259005..e849ec4a83 100644 --- a/persistence-modules/spring-jpa/README.md +++ b/persistence-modules/spring-jpa/README.md @@ -5,7 +5,7 @@ - [JPA Pagination](https://www.baeldung.com/jpa-pagination) - [Sorting with JPA](https://www.baeldung.com/jpa-sort) - [Self-Contained Testing Using an In-Memory Database](https://www.baeldung.com/spring-jpa-test-in-memory-database) -- [A Guide to Spring AbstractRoutingDatasource](https://www.baeldung.com/spring-abstract-routing-data-source) +- [Obtaining Auto-generated Keys in Spring JDBC](https://www.baeldung.com/spring-jdbc-autogenerated-keys) - [Spring Data Annotations](http://www.baeldung.com/spring-data-annotations) - More articles: [[next -->]](/spring-jpa-2) diff --git a/persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientDataSourceRouter.java b/persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientDataSourceRouter.java deleted file mode 100644 index a9f5d83b55..0000000000 --- a/persistence-modules/spring-jpa/src/main/java/com/baeldung/dsrouting/ClientDataSourceRouter.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.baeldung.dsrouting; - -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; - -/** - * Returns thread bound client lookup key for current context. - */ -public class ClientDataSourceRouter extends AbstractRoutingDataSource { - - @Override - protected Object determineCurrentLookupKey() { - return ClientDatabaseContextHolder.getClientDatabase(); - } -}