Custom DataSource Spring boot - spring

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

Related

Spring boot application.properties naming

I am learning about springboot and trying to connect to a DB2 database. I got that working just fine.
Below are my working DB2 properties:
spring.datasource.url=jdbc:db2://server:port/database:currentSchema=schema-name;
spring.datasource.username=user1
spring.datasource.password=password1
But I renamed them to start with "db2" instead "spring" like:
db2.datasource.url=jdbc:db2://server:port/database:currentSchema=schema-name;
db2.datasource.username=user1
db2.datasource.password=password1
My app still runs, hHowever, when I do that, my controllers no longer return results as they did before the rename.
The reason I ask this is that if I add 2nd data source in the future, I could distinguish easily properties by their data sources if I name them like this.
UPDATE:
Thanks to #Kosta Tenasis answer below and this article (https://www.javadevjournal.com/spring-boot/multiple-data-sources-with-spring-boot/), I was able to resolve and figure this out.
Then going back to my specific question, once you have the configuration for data source in place, you can then modify application.properties to have:
db2.datasource.url=...
instead of having:
spring.datasource.url=...
NOTE1: if you are using Springboot 2.0, they changed to use Hikari and Hikari does not have url property but instead uses jdbc-url, so just change above to:
db2.datasource.jdbc-url=...
NOTE2: In your datasource that you had to create when adding multiple datasources to your project, you will have annotation #ConfigurationProperties. This annotation needs to point to your updated application.properties for datasource (the db2.datasource.url).
By default Spring looks for spring.datasource.** for the properties of the DataSource to connect to.
So you might be getting wrong results because you are not connecting to the database. If you want to configure a DataSource with different,from default, properties you can do like so
#Configuration
public class DataSourceConfig {
#Bean
#ConfigurationProperties(prefix="db2.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create()
.build();
}
And let's say a day comes along and you want a second DataSource you can modify the previous class to something like:
#Configuration
public class DataSourceConfig {
#Bean
#ConfigurationProperties(prefix="db2.datasource")
public DataSource d2Datasource() {
return DataSourceBuilder.create()
.build();
}
#Bean
#ConfigurationProperties(prefix="db3.datasource")
public DataSource db3Datasource() { //pun intented
return DataSourceBuilder.create()
.build();
}
}
and after that in each Class that you want a DataSource you can specify which of the beans you like:
public class DB3DependedClass{
private final DataSource dataSource;
public DB3DependedClass(#Qualifier("db3Datasource") DataSource dataSource){
this.dataSource = dataSource;
}
}
So by default spring will look for
spring.datasource.url (or spring.datasource.jdbc-url)
spring.datasource.username
spring.datasource.password
If you specify another DataSource of your own, those values are not needed.
So in the above example where we specified let's say db3.datasource spring will look for
db3.datasource.url
db3.datasource.username
db3.datasource.password
Important thing here is that the spring IS NOT inferred meaning the complete path is indeed: db3.datasource.url
and NOT
spring.db3.datasource.url
Finally to wrap this up you do have the flexibility to make it start with spring if you want so by declaring a prefix like spring.any.path.ilike.datasouce and of course under that the related values. Spring will pick up either path as long as you specify it.
NOTE: This answer is written solely in the text box provided here and was not tested in an IDE for compilation errors. The logic still holds though

spring boot with multiple databases

I'm trying to write an application that accesses data from two sources. I'm using Spring Boot 2.3.2. I've looked at several sources for info about how to configure the app: the Spring documentation talks about setting up multiple datasources, but does not explain how to link up JPA repositories. This Baeldung article goes a lot further, but I'm looking to take advantage of autoconfiguration in Spring.
So far, I've created a separate package, added a config class (along with model and repositories), and included this package in scanBasePackages so that it's picked up. Since I'll have more than one datasource, I've added this to my #SpringBootApplication:
#Bean
#Primary
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
This successfully loads up my Spring app using the standard spring config values. The two databases are on different servers, but should share characteristics (other than url and credentials).
So, my auxiliary configuration file looks like this
#Configuration
#EnableAutoConfiguration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "orgEntityManagerFactory",
transactionManagerRef = "orgTransactionManager",
basePackages = {
"pacage2.repositories"
}
)
public class DataSourceConfiguration {
// added because of this answer: https://stackoverflow.com/a/51305724/167889
#Bean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder() {
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), new HashMap<>(), null);
}
#Bean
#ConfigurationProperties(prefix = "external.datasource")
public DataSourceProperties orgDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
public HikariDataSource orgDataSource(#Qualifier("orgDataSourceProperties") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class)
.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean orgEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("orgDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("package2.model")
.build();
}
#Bean
public PlatformTransactionManager orgTransactionManager(
#Qualifier("orgEntityManagerFactory") EntityManagerFactory entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Now, the error I'm getting right now is Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set. However, I have that value in my config and it's applied by the Spring auto config. I believe it needs to be set in the EntityManagerFactoryBuilder and by creating my own, the autoconfig isn't getting applied.
How can I have my cake and eat it too? I'd like to leverage as much of the robust autoconfiguration that Spring provides to setup datasources and wire them to the appropriate repositories. Effectively, all that I want to change is the url and credentials, and I can separate the entities and repositories into a completely separate package for easy scanning.

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

How do you autowire/inject your datasource in Spring-boot?

I have been working with Spring boot for a bit now, and the datasource is always configured in your application.properties in every example I have seen, kind of like this:
# DataSource configuration
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/abcdef
spring.datasource.username=******
spring.datasource.password=******
However, lately I have been trying to integrate Spring Social, and the examples I have seen configure it in java in a config file like this:
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.url"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
This allows for the datasource object to later be injected or autowired into the social config as seen here for example.
My question is, do I need to configure a datasource bean like this to be able to later inject the datasource, or will Spring-boot handle that for me?
Not a Spring (or Boot) expert by any means, but Spring Boot will auto-provide a Bean of type DataSource if the properties are there and there's a requirement for it. To use it you just #Autowire it.
Try this . If there are multiple #Configuration in springboot , You can import the the other config(DataSourceConfig) into your main AppConfig.
And then Using #PropertySource pull in the db url,username,password etc
https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s03.html
#Configuration
#Import(DataSourceConfig.class)
#PropertySource("classpath:application.properties")
public class SpringbatchConfig {
#Autowired
DataSourceConfig dataSourceConfig;
#Bean
public void myService myService() {
return new myServiceImpl(dataSourceConfig.dataSource());
}
}

Resources