Create dynamic DataSource in spring - spring

I would like to create data source object on the fly.
I won't be having data base details during compilation time, so I can't put this in either application.properties or hard code it in the code.
To put it simple, assume that I will get the details by calling a web service, by using those details I need to create data source(schema's will be same for all the urls).
#Configuration
public class DataBaseConfig {
#Bean
public DriverManagerDataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
DataBaseDetails details = someWebServiceCall;
dataSource.setUrl(details.getURL());
dataSource.setUsername(details.getUserName());
dataSource.setPassword(details.getPassword());
return dataSource;
}
}
This dataSource() would be called only during application start, is there any way to call to method dataSource for a session/request?
BR,
Kitty

Related

How can I create a db2 connection in Spring boot without use application.properties?

I need to build a connection to db2 database, but I can not use application.properties at this moment, so how can I create this connection?
You can implement a datasource bean in the app itself. For example:
#Configuration
public class MyClass {
#Bean
public DataSource getDataSource() {
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName(JDBC_DRIVER_CLASS_NAME);
dataSourceBuilder.url(myprops.getJDBCUrl());
dataSourceBuilder.username(myprops.getJDBCUsername());
dataSourceBuilder.password(myprops.getJDBCPassword());
return dataSourceBuilder.build();
}
}
Then, make sure any autowired references to the data source are "lazy", so the app has time to load the properties (maybe from another file) and to instantiate the data source, as in:
#Lazy
#Autowired
private DataSource dataSource;

Custom DataSource Spring boot

spring works well when we use the default datasource and ways we can use in-build spring jpa.
So currently what we do is the following
specify the config for DB in the application.properties
myapp.datasource.url=jdbc:mysql:thin:#localhost:1521:myschema
myapp.datasource.username=user
myapp.datasource.password=password
myapp.datasource.driver-class=com.mysql.cj.jdbc.Driver
Custom datasource
#ConfigurationProperties(prefix = "myapp.datasource")
#Bean
public DataSource mySqlDataSource()
{
return DataSourceBuilder.create().build();
}
We have the same application running for multiple clients. Problem is each client has their own DB schema.
So, the problem now is that we need to be able to serve each client but in order to do this, we need to create multiple datasources
for instance:
#ConfigurationProperties(prefix = "myapp.partner1.datasource")
#Bean
public DataSource mySqlDataSourcePartner1()
{
return DataSourceBuilder.create().build();
}
#ConfigurationProperties(prefix = "myapp.partner2.datasource")
#Bean
public DataSource mySqlDataSourcePartner2()
{
return DataSourceBuilder.create().build();
}
#ConfigurationProperties(prefix = "myapp.partner3.datasource")
#Bean
public DataSource mySqlDataSourcePartner3()
{
return DataSourceBuilder.create().build();
}
and so on...
Is there a generic and more efficient way of doing this? where if in future when a new partner is added we can just specify the config in application properties and get that working?
You can use Spring Boot Multi-tenancy model using a separate database for each client. You can save the database configuration in config-properties or database then depending upon the ClientId you can you the Datasource. You need to add Interceptor to intercept the Request and identify the tenant. Please refer to the below example
https://tech.asimio.net/2017/01/17/Multitenant-applications-using-Spring-Boot-JPA-Hibernate-and-Postgres.html
please check
https://github.com/sumanentc/multitenant

How to create custom datasource for each session

I would like to create a stateless bean in Spring boot which enable user to connect to a specific database, so I started with this code, but i'm still new with spring boot.
#Bean
#Primary
public DataSource helloDataSource() {
return DataSourceBuilder.create()
.username("myUsername")
.password("myPassword")
.driverClassName("myDBDriver")
.build();
}
So what is the best way to follow to make this code work and connect to any database ( Also remote Database )
There are several ways that this could be done. One of my preferred way is to provide configuration of data sources via properties file. Here is a sample property file for postgresql:
pg.datasource.url=jdbc:postgresql://db-server-bar:5432/app-db
pg.datasource.username=root
pg.datasource.password=toor
pg.datasource.driver-class-name=org.postgresql.Driver
Now you can create configuration class for each datasource:
public class BarDbConfig {
#Bean(name = "pgDataSource")
#ConfigurationProperties(prefix = "pg.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "barEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean
barEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("pgDataSource") DataSource dataSource
) {
return
builder
.dataSource(dataSource)
.packages("com.app.domain")
.persistenceUnit("pg")
.build();
}
}
For a detailed tutorial, you can refer to this tutorial.
There can also be situations where you would like to switch databases at runtime. In which case, you can use something called AbstractRoutingDataSource. A detailed tutorial on how to use this feature can be found at Spring's official blog site.

Will spring Boot datasource properties work if we configure datasource programmatically in Spring Boot

Will Spring Boot datasource properties work if we configure datasource programmatically?
The following properties worked only when I fetch DB configuration from application.properties. If I configure datasource programmatically the following properties are not working.
spring.datasource.tomcat.initial-size=10
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=5
I used the following code to configure datasource programmatically
#ConfigurationProperties(prefix = "spring.datasource")
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder.create().username(userName).password(password).url(url).driverClassName(driverName)
.build();
}
To make it work programmatically I used the following code snippet.But I'm not convinced. I feel it is not a cleaner solution. I have to read at least 20 properties from application.properties and add it to PoolProperties.
#ConfigurationProperties(prefix = "spring.datasource")
#Bean
#Primary
public DataSource dataSource() {
PoolProperties poolProperties = new PoolProperties();
poolProperties.setUrl(url);
poolProperties.setDriverClassName(driverName);
poolProperties.setUsername(userName);
poolProperties.setPassword(password);
poolProperties.setTestWhileIdle(false);
poolProperties.setTestOnBorrow(true);
poolProperties.setValidationQuery("SELECT 1 FROM DUAL");
poolProperties.setTestOnReturn(false);
poolProperties.setValidationInterval(30000);
poolProperties.setTimeBetweenEvictionRunsMillis(30000);
poolProperties.setInitialSize(10);
poolProperties.setMaxActive(10);
poolProperties.setMaxIdle(5);
poolProperties.setMinIdle(5);
poolProperties.setMaxWait(10000);
poolProperties.setRemoveAbandonedTimeout(60);
poolProperties.setMinEvictableIdleTimeMillis(30000);
poolProperties.setLogAbandoned(true);
poolProperties.setRemoveAbandoned(true);
DataSource datasource = new DataSource(); // import org.apache.tomcat.jdbc.pool.DataSource;
datasource.setPoolProperties(poolProperties);
return datasource;
}
It there way we can make the following default Spring Boot properties work?
spring.datasource.tomcat.initial-size=10
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=5
remember to give tomcat from properties for db in property files like this.
# Oracle DB - "foo"
spring.datasource.tomcat.url=jdbc:oracle:thin:#//db-server-foo:1521/FOO
spring.datasource.tomcat.username=fooadmin
spring.datasource.tomcat.password=foo123
spring.datasource.tomcat.initial-size=10
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=5
then configure datasource like this.
/**
* Auto-configured DataSource
*/
#ConfigurationProperties(prefix = "spring.datasource.tomcat")
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}

#Refreshscope with Datasource configuration

I am having a datasource configuration class in a Spring boot app. Snippet below
My configuration is fetched from Spring cloud config server. When I change my DB hostname and refresh using /refresh endpoint, the app is NOT using new DB host. ANy idea why ?
#Configuration
#RefreshScope
public classe DBConfig
{
#Resource
private Environment env;
private DataSource ehubDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("datasource.driverClassName"));
dataSource
.setUrl(env.getProperty("datasource.url"));
dataSource.setUsername(env.getProperty("datasource.username"));
dataSource.setPassword(env.getProperty("datasource.password"));
return dataSource;
}
}
As per docs,#RefreshScope will technically work on #Configuration, provided anything that depends on those beans cannot rely on them being updated when a refresh is initiated, unless it is itself in #RefreshScope
So could you please check your "Environment.java", You may forget to specify #RefreshScope in Environment.java. Please share your Environment.java if it is not working.
Normally, the #Configuration class contains beans, which means the datasource method should be marked as #Bean. You need #RefreshScope on each bean.
For a datasource, you probably want #ConfigurationProperties, rather than writing code for each property. #ConfigurationProperties automatically includes #RefreshScope, so you actually don't need RefreshScope here.
With #ConfigurationProperties almost no code is needed.
#Configuration
public class DBConfig
{
#Bean
#ConfigurationProperties("datasource")
public DataSource ehubDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
return dataSource;
}
}
If your Environment does something other than read the properties files, then this may not work for you.
If you want the bean name to be different from the method name, you can provide a parameter to #Bean. The code below creates the same bean as above.
#Bean(name = "ehubDataSource")
#ConfigurationProperties("datasource")
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
return dataSource;
}

Resources