Spring-boot 2 ignoring Hikari specific properties - spring-boot

I'm trying to configure some Hikari specific properties for the datasource, and can't figure out how to do that left alone, that I don't even see how it suppose to work.
According to the Spring-boot doc you should be able to configure lots of additional props, for example, spring.datasource.hikari.transaction-isolation. So I have application.properties file with a content
spring.application.name=My App
# DB
spring.datasource.url=jdbc:sqlserver://localhost;databaseName=dbname
spring.datasource.username=user
spring.datasource.password=passw
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.maximumPoolSize=30
spring.datasource.hikari.transaction-isolation=TRANSACTION_READ_COMMITTED
From what I understand from the docs, this should be enough for Spring-boot 2 to initialize Hickory datasource with the additional properties. However, when I run my application I can see that the datasource created with only generic spring.datasource.* properties.
I debugged application initialisation and looked at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration class. I can see Spring uses Hikari datasource and creates an instance of HikariDataSource.class, but created datasource doesn't have any specific properties set. What I don't understand, is how it even possible to have some additional properties if the properties parameter has the actual type of DataSourceProperties which has no knowledge of any hikari specific properties. I thought it would be something hikari specific but it is not.
/**
* Hikari DataSource configuration.
*/
#Configuration(proxyBeanMethods = false)
#ConditionalOnClass(HikariDataSource.class)
#ConditionalOnMissingBean(DataSource.class)
#ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
#Bean
#ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
I can imagine that there maybe another call to configure the datasource later, but I can't find it and when inspecting datasource, at runtime, I can see they not set. I must be missing something here, so any help would be appreciated.
P.S. I also tried to use manual configuration but hit an another issue with Flyway.

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

how does spring boot auto configure the driver of a special datasource?

Without spring boot ,we must specify the detail of a data source,right?
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
}
With spring boot,we even do not need do anything,I know spring boot will detect whether there is a jar contains a data source to decide create a data source bean or not.I see the source code from org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration :
#Configuration(proxyBeanMethods = false)
#Conditional(PooledDataSourceCondition.class)
#ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
#Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
But my question is how does spring boot know the driver class or url and else for every different database?I can not find any specification from spring-boot-autoconfigure-2.5.0.jar
From DataSource Configuration in the docs:
Spring Boot can deduce the JDBC driver class for most databases from the URL. If you need to specify a specific class, you can use the spring.datasource.driver-class-name property.
So start without configuring anything and just putting the JDBC driver on the classpath. If it would not work, you can manually configure it.

Warning: Not loading a JDBC driver as driverClassName property is null, in springboot working with two datasources

I have currently configured spring boot to work with two different datasources. The application is working fine, however when I start the spring boot application I get an warning repeated 10 times like below:
2018-06-05 10:28:15.897 WARN 8496 --- [r://myScheduler] o.a.tomcat.jdbc.pool.PooledConnection : Not loading a JDBC driver as driverClassName property is null.
As I mentioned this is not affecting my application, but I would like to know why I am having this kind of warning and if there is any way to fix it.
When using two or more datasources you need to configure them yourself. In that case Spring Boot's DataSourceAutoConfiguration (and also DataSourceProperties) won't be used.
You most probably have the related DB details like the name of the JDBC driver class name in application.properties file as follows:
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
primary.datasource.url=jdbc:sqlserver://xxx.yyy.zzz.www:1433;databaseName=primaryDB
primary.datasource.username=username
primary.datasource.password=password
other.datasource.url=jdbc:sqlserver://xxx.yyy.zzz.www:1433;databaseName=otherDb
other.datasource.username=otheruser
other.datasource.password=otherpassword
Thus, to set the driver class name for the datasource just say:
#Value("${spring.datasource.driver-class-name}")
String driverClassName;
#Primary
#Bean(name = "primaryDb")
#ConfigurationProperties(prefix = "primary.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().driverClassName(driverClassName).build();
}
#Bean(name = "otherDb")
#ConfigurationProperties(prefix = "other.datasource")
public DataSource otherDataSource() {
return DataSourceBuilder.create().driverClassName(driverClassName).build();
}

what is the Difference between spring.profiles.active=production vs spring.profiles.active=local

When I try to run an spring boot application.
There is a property available inapplication.properties file where it has the property spring.profiles.active=production.
While search the details about this property in web I got to know that spring.profiles.active=local.
Can anyone kindly explain these details?
Certain environment-specific choices made for development aren’t appropriate or won’t work when the application transitions from development to production.
Consider database configuration, for instance. In a development environment,
you’re likely to use an embedded database preloaded with test data like this:
#Bean(destroyMethod="shutdown")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
In a production setting,
you may want to retrieve a DataSource from your container using JNDI:
#Bean
public DataSource dataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
Starting with Spring 3.1 you can use profiles. Method-leve #Profile annotation works starting from Spring 3.2. In Spring 3.1 it's only class-level.
#Configuration
public class DataSourceConfig {
#Bean(destroyMethod="shutdown")
#Profile("development")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
#Bean
#Profile("production")
public DataSource jndiDataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
}
Each of the DataSource beans is in a profile and will only be created if the prescribed profile is active. Any bean that isn’t given a profile will always be created, regardless of what profile is active.
You can give any logical names to your profiles.
You can use this property to let Spring know which profiles should be active ( to be used while starting application). For example, if you give it in application.properties or via argument -Dspring.profiles.active=prod; you tell Spring, to run under prod profile. Which means - Spring will look for "application-prod.yml" or "application-prod.properties"file and will load all properties under it.
You can also annotate bean (method or class) by #Profile("PROFILE_NAME") - this ensures, the bean is mapped to certain profile.
You can pass multiple profiles to spring.profiles.active.
More information in docs - https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

Setting Tomcat Pool properties dynamically

We have a multitenant application with multiple datasources and want to configure the data pool properties (maxActive, minIdle, etc.) individually for each.
Currently I'm constructing a org.apache.tomcat.jdbc.pool.DataSource and setting a few properties manually such as username and password with dataSource.setUserName() and dataSource.setPassword(). I'd like to set the rest of the properties by loading the configuration from a string, for example minIdle=20;initialSize=15.
There are two methods on DataSource which appear like they would accomplish this, but don't seem to be doing what I expect them to. I tried dataSource.setConnectionProperties("..") with some properties as well as populating a Properties object and passing it to dataSource.setDbProperties(), though neither seemed to have an effect when I viewed the pool attributes through JMX. I was only able to change these properties through the specific setters such as dataSource.setInitialSize().
The only way I can think of to set each of the properties from a string of them without the above attempts working is to iterate through each of the properties and have if-else or switch-case logic to determine which of the dataSource setters to call to set the value.
So is there a way to dynamically set these properties from a string without calling each individual setter?
When I set the username either of the setConnectionProperties or setDbProperties, it did change, but I think this may be specific for things like username and password as the other properties I tried to set didn't have an effect.
edit: To clarify, data source properties will be loaded from the database and a new datasource may be added on the fly, so using application properties won't work.
I'm assuming that you are working on spring-boot project. In that case,
Create properties in the application.properties file of spring-boot
spring.datasource.username=XXX
spring.datasource.password=XXX
spring.datasource.max-active=XXX
spring.datasource.min-idle=XXX
and create a configuration file to create datasource as shown below
#Configuration
public class DataSourceConfiguration {
#Value("${spring.datasource.url}")
private String url;
#Value("${spring.datasource.driverClassName}")
private String driverClass;
#Value("${spring.datasource.username}")
private String username;
#Value("${spring.datasource.password}")
private String password;
#Value("${spring.datasource.min-idle}")
private Long minIdle;
#Bean
#Primary
public DataSource dataSource() {
DataSource dataSource = new DataSource();
dataSource.setJdbcUrl(url);
dataSource.setDriverClassName(driverClass);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setMinimumIdle(minIdle);
return dataSource;
}
}
Like this, you can create multiple properties with different names and refer it while creating different Beans of datasource.
If you want, you can place the application.properties file outside of your project and access it using #PropertySource annotation in your main class.
I'd use the Spring Boot ConfigurationProperties support. From the manual:
74.2 Configure Two DataSources
Creating more than one data source works the same as creating the first one. You might want to 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();
}
If you're concerned about having loads of similar properties in a properties file, the Spring config server (which allows them to be set up and versioned in GIT) might help.

Resources