Spring Configuration Metadata - spring

I am setting up two data sources as shown here at http://docs.spring.io/spring-boot/docs/1.3.0.M2/reference/htmlsingle/#howto-two-datasources using spring boot, but when doing so my application.properties shows warnings that for example x.x.username is an unknown property. This is correct to some extent as javax.sql.DataSource does not contain url, username, password, etc. but the implementation classes do. I have annotation processor set up and it works fine when working with concrete classes.
I notice that org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$NonEmbeddedConfiguration uses both DataSourceProperties and has #ConfigurationProperties annotated on dataSource(). This would probably get rid of my warnings but what is the point of this. Isn't it setting the properties twice this way?
Config:
#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();
}
Properties with warnings:
datasource.primary.url=jdbc:...
datasource.primary.username=user
datasource.primary.password=password
datasource.secondary.url=jdbc:...
datasource.secondary.username=user
datasource.secondary.password=password

Since someone bothered to +1 this question I thought I'd post a solution. Note that I think the #ConfigurationProperties on the DataSources themselves are unecessary because they are already set on the DataSourceProperties which is used to build the DataSource, but I left it in there because that's how the Spring team has done it in org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$NonEmbeddedConfiguration. My only guess why would be if your DataSource had additional properties that could be set other than what's exposed in DataSourceProperties, but then you would get warnings in the "Spring Boot application.properties editor" for those properties.
Note that DataSourceBuilder will use Tomcat, HikariCP or Commons DBCP in that order if found on Classpath as DataSource unless you specify something else with dataSourceBuilder.type(Class<? extends DataSource>)
Properties:
datasource.primary.url=jdbc:...
datasource.primary.username=user
datasource.primary.password=password
datasource.secondary.url=jdbc:...
datasource.secondary.username=user
datasource.secondary.password=password
Java Config:
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource.primary")
public DataSourceProperties primaryProps() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties(prefix = "datasource.secondary")
public DataSourceProperties secondaryProps() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties(prefix = "datasource.primary")
public DataSource secondaryDataSource() {
DataSourceProperties props = secondaryProps();
return DataSourceBuilder.create(props.getClassLoader())
.driverClassName(props.getDriverClassName())
.url(props.getUrl())
.username(props.getUsername())
.password(props.getPassword())
.build();
}
#Bean
#ConfigurationProperties(prefix = "datasource.primary")
public DataSource secondaryDataSource() {
DataSourceProperties props = secondaryProps();
return DataSourceBuilder.create(props.getClassLoader())
.driverClassName(props.getDriverClassName())
.url(props.getUrl())
.username(props.getUsername())
.password(props.getPassword())
.build();
}

Related

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.

h2 database config NOT read by springBoot IOC

I am working on some POC for which I am using h2 database with following details in application.properties file:
#datasource
ds.pc.jdbcUrl=jdbc:h2:mem:test;MODE=oracle;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
ds.pc.type=com.zaxxer.hikari.HikariDataSource
ds.pc.driver-class-name=org.h2.Driver
ds.pc.hikari.pool-name=pc-hikari-dbcp
ds.pc.hikari.maximum-pool-size=20
ds.pc.driver-type=thin
ds.pc.validation-query=Select 1 from dual
ds.pc.primary=true
ds.pc.username=sa
ds.pc.password=
spring.h2.console.enabled=true
spring.h2.console.path=/h2
I have defined my beans as below:
#Configuration
public class AppConfig
{
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
{
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public ConversionServiceFactoryBean conversionServiceFactoryBean()
{
return new ConversionServiceFactoryBean();
}
#Primary
#Bean(name = "pcJdbcTemplate")
public NamedParameterJdbcTemplate pCJdbcTemplate()
{
return new NamedParameterJdbcTemplate(pCDataSource());
}
#Primary
#Bean
#ConfigurationProperties(prefix = "ds.pc")
public DataSource pCDataSource()
{
return DataSourceBuilder.create().build();
}
}
It fails with below exception:
java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
at com.zaxxer.hikari.HikariConfig.validate(HikariConfig.java:958) ~[HikariCP-3.4.2.jar:?]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:109) ~[HikariCP-3.4.2.jar:?]
at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:158) ~[spring-jdbc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
But when I hard code the data source properties as below then it works:
#Primary
#Bean
public DataSource pCDataSource()
{
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName("org.h2.Driver");
hikariConfig.setUsername("sa"); hikariConfig.setPassword("");
hikariConfig.setJdbcUrl(
"jdbc:h2:mem:test;MODE=oracle;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
return new HikariDataSource(hikariConfig);
}
Tried with changing properties as below:
ds.pc.jdbc-url=jdbc:h2:mem:test;MODE=oracle;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
ds.pc.url=jdbc:h2:mem:test;MODE=oracle;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
Still it doesn't work.
What's wrong in my application.properties file that it's not being read?
Also I am not able to see h2 console at localhost:8080/h2
The problem was that my application.properties file was under a folder named appConfig under src/main/resources.
Solution 1: Rename appConfig to config .
Go to the section 2.3. Application Property Files at documentation. It explains why config folder works.
Solution 2: move properties files directly under src/main/resources
Silly me. Had spent quite few hours to just find things can be simple.

Hikari - Spring Boot is ignoring hikari properties

I have a Spring Boot 2 application which has two datasources. Both datasources work, however I am unable to change properties such as maximum-pool-size, my changes are not taking effect.
I am configuration my two datasources in my application.properties file;
spring.datasource.url=jdbc:sqlserver://server;databaseName=ProductionMetrics
spring.datasource.username=ProductionMeusernametricsUser
spring.datasource.password=password
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.hikari.maximum-pool-size=20
# Products
trwbi.datasource.url=jdbc:sqlserver://server;databaseName=TRWBI
trwbi.datasource.username=username
trwbi.datasource.password=password
trwbi.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
trwbi.datasource.hikari.maximum-pool-size=20
Then, I'm setting up the two datasources like this;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "defaultEntityManagerFactory",
transactionManagerRef = "defaultTransactionManager",
basePackages = {
"com.domain.visualisation.shared.repository"
}
)
public class DefaultDbConfig {
#Primary
#Bean(name = "defaultDataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource defaultDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "defaultEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean
entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("defaultDataSource") DataSource dataSource
) {
return builder
.dataSource(dataSource)
.packages("com.domain.visualisation.shared.entities")
.persistenceUnit("default")
.build();
}
#Primary
#Bean(name = "defaultTransactionManager")
public PlatformTransactionManager defaultTransactionManager(
#Qualifier("defaultEntityManagerFactory") EntityManagerFactory defaultEntityManagerFactory
) {
return new JpaTransactionManager(defaultEntityManagerFactory);
}
}
When I turn on debugging for Hikari, I can see that the maximumPoolSize value is still 10, rather than the value of 20 that I have defined. I've tried to apply other properties such as leak-detection-threshhold, pool-name and idle-timeout, but neither of those are being applied either.
Why are they not being applied?
You should use property name maximumPoolSize
spring.datasource.hikari.maximumPoolSize=20
maximumPoolSize
This property controls the maximum size that the pool is allowed to reach, including both idle and in-use connections.
In case of multiple datasources and because you are using DataSourceBuilder the following works.
spring.datasource.maximum-pool-size=20
trwbi.datasource.maximum-pool-size=20
Also in your case I would recommend to turn off autoconfiguration:
#SpringBootApplication(scanBasePackages = {...},
exclude = {DataSourceAutoConfiguration.class} )
#EnableSwagger2
public class Application {
....
If you don't use builder, but use autoconfiguration, then use
spring.datasource.hikari.minimumIdle=1
spring.datasource.hikari.maximum-pool-size=3

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 to change expected datasource property name in Spring

I would like to change the expected property name in Spring for data source from:
spring.datasource.url: jdbc:oracle:thin:#127.0.0.1:151:xe
to:
com.foo.bar.spring.datasource.url: jdbc:oracle:thin:#127.0.0.1:151:xe
i.e. have a certain prefix for all spring properties in my application.
Is that possible? If so, how.
Thanks in advance!
Write a configuration class where you instantate your DataSource per #Bean annotation like this:
#Configuration
public MyConfig {
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "com.foo.bar.spring.datasource")
public DataSource dataSource() {
DataSource dataSource = DataSourceBuilder.create().build();
return dataSource;
}
}
this should do the trick..
see spring doc here:

Resources