Spring multiple Data Source configuration - Auto Fail over mechanism - java

I would like to know if Spring offers any support for Auto Fail over of Data Sources? For example, if the primary data source is down, connect to secondary. Any suggestions to effectively configure auto fail over of Data sources is greatly appreciated.
Thanks.

The Primary/DR setup for DB should be managed at DB level. Its not right to switch that in code. But to answer your question "can connect to 2 Data sources in Spring". Yes you can easier if you are using Spring-boot.
Few details from spring documentation.
Mark one of them as #Primary if you are using the default auto-configuration for JDBC or JPA (then that one will be picked up by any #Autowired injections).
#Bean
#Primary
#ConfigurationProperties(prefix="datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}

Related

How does JPA entityManager choose Connection Pool in Spring Boot Application?

I have 2 DAO - the first works with #Bean DataSource + JDBC. Configuration is the following:
#Bean("dataSource")
#Singleton
public DataSource getDataSource() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("...");
basicDataSource.setUrl("...");
basicDataSource.setUsername(...);
basicDataSource.setPassword(...);
...
return basicDataSource;
}
The second works with entityManager. application.properties configuration is the following:
spring.datasource.url=...
spring.datasource.username=...
spring.datasource.password=...
...
When I starts my Spring Boot Application and spring initializes my beans, I use the second DAO to get some information from database.
I am using second DAO -> entityManager in this case.
I expects that entityManager uses configuration from application.properties.
Indeed, entityManager uses configration from bean DataSource.
How does It work?
p.s. database properties in application.properties look like used.
Actually I think that I should use one ConnectionPool for my application.
I can configure DataSource as #Bean and provide entityManager and jdbcTemplate with It.
Should I choose another solution? Or Is this idea quite suitable?
It's because of the importance. #Configuration has higher precedence than application.properties. First spring-boot searches for #Bean definition, if it's not found, then it checks application.properties. Generally those definitions are equivalent.

Proper setting transaction manager with multitenant databases configuration with spring-boot

I have multitenant database in Spring Boot. I store multi spring JDBC templates (based on tomcat Data Sources, configured manually) in map (immutable bean). And I choose proper data source based on uuid in a request (connection pool per database). I have disabled standard configuration in Spring Boot by:
#SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
What is the proper way of transaction manager configuration? With single data source I can use PlatformTransactionManager, but how it should be done with multiple jdbc templates/data sources in spring? It would be the best if I could set everything dynamically. Thanks in advance.
Here a solution for using multiple datasources
http://www.baeldung.com/spring-data-jpa-multiple-databases
Configure Two DataSources
If you need to configure multiple data sources, you can apply the same tricks that are described in the previous section. You must, however, mark one of the DataSource #Primary as various auto-configurations down the road expect to be able to get one by type.
If you create your own DataSource, the auto-configuration will back off. In the example below, we provide the exact same features set than what the auto-configuration provides on the primary data source
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSourceProperties fooDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSource fooDataSource() {
return fooDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
#ConfigurationProperties("app.datasource.bar")
public BasicDataSource barDataSource() {
return (BasicDataSource) DataSourceBuilder.create()
.type(BasicDataSource.class).build();
}
fooDataSourceProperties has to be flagged #Primary so that the database initializer feature uses your copy (should you use that).
app.datasource.foo.type=com.zaxxer.hikari.HikariDataSource
app.datasource.foo.maximum-pool-size=30
app.datasource.bar.url=jdbc:mysql://localhost/test
app.datasource.bar.username=dbuser
app.datasource.bar.password=dbpass
app.datasource.bar.max-total=30

Spring boot reset datasource on the fly

I am trying to update datasource in Spring Boot when the DB property like DB name, password or hostname changes in the spring configuration file or custom DB property file. When the property changes the application has to update by its own by listening changes to property.
I was using Spring actuator to /restart beans once the DB configuration is changed. But user has to explicitly make a post request to restart. This step has to be avoided by listening to the changes and update datasource.
Can you tell me the best way to do this in Spring boot?
Found a way to update datasource on-the-fly,
I have given external spring config file which contains DB properties to the application and then refreshed the properties using #RefreshScope for the datasource bean.
A thread monitors the file changes and makes a call to actuator refresh() method.
database.properties
dburl=jdbc://localhost:5432/dbname
dbusername=user1
dbpassword=userpwd
Creating datasource,
#RefreshScope
public class DBPropRefresh {
#Value("${dburl}")
private String dbUrl;
#Value("${dbusername}")
private String dbUserName;
#Value("${dbpassword}")
private String dbPassword;
#Bean
#RefreshScope
public DataSource getDatasource() {
return new DatasourceBuilder().create().url(dbUrl).username(dbUserName).password(dbPassword);
}
}
Giving external config file to the application,
java -jar myapplication.jar --spring.config.location=database.properties
I have created a Java thread class to monitor database.properties file changes. Followed https://dzone.com/articles/how-watch-file-system-changes
When there are changes then it makes call to refreshEndPoint.refresh().
In pom.xml,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
You can use Spring's Dynamic Data Source routing and check if it helps? It's a very old technique and might come handy, if that serves your purpose.
But please note that - this is data source routing and not new data source configuration.
https://spring.io/blog/2007/01/23/dynamic-datasource-routing/
In my project I used multitenancy . Basically I defined several datasources in properties like this:
primary.datasource.url=jdbc:postgresql://localhost:5432/db_name?currentSchema=schema_name
primary.datasource.username=user
primary.datasource.password=password
primary.datasource.driverClassName=org.postgresql.Driver
primary.datasource.driver-class-name=org.postgresql.Driver
secondary.datasource.url=jdbc:postgresql://localhost:5432/other_db?currentSchema=schema
secondary.datasource.username=user
secondary.datasource.password=password
secondary.datasource.driverClassName=org.postgresql.Driver
secondary.datasource.driver-class-name=org.postgresql.Driver
default.datasource.url=jdbc:postgresql://localhost:5432/default_db?currentSchema=public
default.datasource.username=user
default.datasource.password=password
default.datasource.driverClassName=org.postgresql.Driver
default.datasource.driver-class-name=org.postgresql.Driver
then in configuration class defined multiple datasources:
#Bean
#Primary
#ConfigurationProperties(prefix="primary.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="secondary.datasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="default.datasource")
public DataSource defaultDataSource(){
return DataSourceBuilder.create().build();
}
and configured multitenancy basing on this and this article.
Pros:
Easy tenant switch which could be triggered manually or even configured to be triggered on some specific header in request (filters).
Could be cofigured to switch between schemas or databases.
Happens dynamically ( you don't have to restart your beans )
Cons:
You have to define all db possibilities in property file.
You have to turn off schema validation because it will go nuts.

How to Configure Spring Oauth2 for Multi tenant

I'm currently developing multi-tenant application using spring and using oauth. Every tenant will have different database. If my url is tenant1.xxx.com, then it will use tenant1 database, etc.
My request already successfully routed using AbstractRoutingDataSource but not the authentication. So when I ask an access token, it still using the default datasource. I think my problem is in oauth2 configuration that set datasource like TokenStore, etc.
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
Is there anyway that token store can choose datasource according to current tenant?
Let me try to give solutions
1) If you check to code inside the JdbcTokenStore then we will find that it's using inside the JdbcTempaltes so JPA or Hibernate routing will not work for that in that case a part of the solution you have to implements org.springframework.security.oauth2.provider.token.TokenStore with JPA implementation.
2) If you do not like to do a solution No 1 then you can also implement AbstractRoutingDataSource and while creating TokenStore passed the routing data source.

How to get Spring Data to handle multiple heterogeneous DataSources?

I've been successful using using the Accessing Data With JPA tutorial for Spring. I've gotten a CrudRepository of my own to work automatically by just configuring a specific DataSource #Bean, and the internal connections between these are managed by Spring Data (or Spring Boot, it's hard to tell which).
However, I can't figure out how to get that automated plumbing to handle a second DataSource #Bean. Injecting a second one causes the autoconfiguration classes to explode during startup.
Any thoughts as to how to do this? The searches I've done for this resulted in articles discussing multiple homogeneous DataSources for load balancing or other purposes, which is really not what I need. I have multiple databases with completely separate content that I need to pull into this app and I'd really like to avoid having to replicate all that automated configuration just because a second database entered the mix.
I'm hoping this is simple, but I'm fearful that it's an unsupported edge case in the autoconfiguration.
You can create two datasources and entitymanagers, one bean of them mark as #Primary
#Configuration
#EnableJpaRepositories(basePackages = "io.eddumelendez.springdatajpa.repository1")
public class FirstConfiguration {
#ConfigurationProperties(prefix = "datasource.postgres")
#Bean
#Primary
public DataSource postgresDataSource() {
return DataSourceBuilder.create().
build();
}
#Bean(name = "entityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean emf1(EntityManagerFactoryBuilder builder){
return builder
.dataSource(postgresDataSource())
.packages("io.eddumelendez.springdatajpa.domain1")
.persistenceUnit("users")
.build();
}
}
Configuration for another datasource:
#Configuration
#EnableJpaRepositories(basePackages = "io.eddumelendez.springdatajpa.repository2", entityManagerFactoryRef = "emf2")
public class SecondConfiguration {
#Bean
#ConfigurationProperties(prefix = "datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean emf2(EntityManagerFactoryBuilder builder){
return builder
.dataSource(mysqlDataSource())
.packages("io.eddumelendez.springdatajpa.domain2")
.persistenceUnit("customers")
.build();
}
}
Your application.properties should looks like this:
datasource.mysql.url=jdbc:mysql://localhost:3306/mysql_demo
datasource.mysql.username=root
datasource.mysql.password=root
datasource.postgres.url=jdbc:postgresql://localhost:5432/postgres_demo
datasource.postgres.username=postgres
datasource.postgres.password=postgres

Categories

Resources