Disable production datasource autoconfiguration in a Spring DataJpaTest - spring

In my application.yml, I have the following configuration (to be able to customize variable on different environment with docker/docker-compose) :
spring:
datasource:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
The trouble is that Spring tries to autoconfigure this datasource while I am in #DataJpaTest, so with an embedded H2 database, and obviously it does not like placeholders....
I tried to exclude some autoconfiguration :
#DataJpaTest(excludeAutoConfiguration =
{DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
But then, nothing works, entityManagerFactory is missing, ...
I could probably use profiles but if possible I preferred another solution.

Did you try defining your own Datasource bean?
#Import(JpaTestConfiguration.class)
#DataJpaTest
#Configuration
public class JpaTestConfiguration{
//...
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:db;DB_CLOSE_DELAY=-1");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
//...
}

Related

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

Spring Boot (Batch) - Inject Datasource

I have a Spring Boot (batch-oriented) application that uses a datasource in order to finalize a batch job and write stuff to the database.
I have the datasource(s) defined inside the application.yml like:
spring:
datasource:
url: jdbc:h2:mem:JavaSpringBootBatch
username: sa
password:
profiles: # default, development, production
active: default, development
---
spring:
h2:
# ...config/settings here
profiles: development
---
spring:
datasource:
# ...datasource config here
profiles: production
The issue is when I try to inject the datasource into one of the Spring config files:
#Configuration
public class PlayerBatchConfig {
...
#Bean
public ItemWriter<Player> writer(final DataSource dataSource) {
final JdbcBatchItemWriter<Player> jdbcItemWriter = new JdbcBatchItemWriter<>();
...
jdbcItemWriter.setDataSource(dataSource);
jdbcItemWriter.setSql(sql.toString());
return jdbcItemWriter;
}
}
...it tells me that:
Could not autowire. There is more than one bean of 'DataSource' type.
Beans:
dataSource (DataSourceConfiguration.class)
dataSource (EmbeddedDataSourceConfiguration.class)
I also tried to inject the datasource like:
#Configuration
public class PlayerBatchConfig {
#Bean
#ConfigurationProperties(prefix = "datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
...
}
...but no luck :( , although the issue with the two datasources goes away eventually.
Any clues how to "circumvent" that?
Since you have 2 datasources you need the annotate them with the #Qualifier the datasources bean and when you use them as well. Spring is telling you it doesn't know which one you want to use.
The DataSource which you want to use annotate it with #Primary, refer spring-boot documentation here for further info. Along with #Primary you might also need to use #Qualifier for controlling the bean injection.

#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;
}

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

Configure h2 for spring boot

I'm trying to configure spring boot to set my test datasource to use h2 in postgresql mode.
I set these lines in my test/resources/application:
spring.datasource.url=jdbc:h2:mem:db1;MODE=PostgreSQL
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
But spring boot keep loading me default h2 configuration.
How can I force spring boot to use my special h2 configuration ?
just do it in java-configuration like this:
#Configuration
#EnableAutoConfiguration
#Profile({ "dev", "demo" })
public class EmbeddedDatabaseConfiguration {
#Bean(name = "dataSource")
public DriverManagerDataSource getDataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName("org.h2.Driver");
driverManagerDataSource.setUrl("jdbc:h2:mem:mylivedata;IGNORECASE=TRUE;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1");
return driverManagerDataSource;
}
}

Resources