Issue
I am using SpringJUnit4ClassRunner to run my integration tests. I had to use two data sources in my Spring Boot REST web service because I want to use different database users for Liquibase and Application queries. When I run the test, the second datasource is not getting created.
Following is my test code
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest
public class DomainServiceImplTest {
@Test
public void testCreateDomain() throws Exception {
}
}
Following is my datasource instantiation
@Configuration
@EnableTransactionManagement
public class DatabaseConfiguration {
private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
@Inject
private Environment env;
@Autowired(required = false)
private MetricRegistry metricRegistry;
@Bean(destroyMethod = "close")
@Primary
public DataSource dataSource(DataSourceProperties dataSourceProperties, NileRegistrarProperties nileRegistrarProperties) {
log.debug("Configuring Datasource");
if (dataSourceProperties.getUrl() == null) {
log.error("Your database connection pool configuration is incorrect! The application" +
" cannot start. Please check your Spring profile, current profiles are: {}",
Arrays.toString(env.getActiveProfiles()));
throw new ApplicationContextException("Database connection pool is not configured correctly");
}
HikariConfig config = new HikariConfig();
config.setDriverClassName(dataSourceProperties.getDriverClassName());
config.setJdbcUrl(dataSourceProperties.getUrl());
if (dataSourceProperties.getUsername() != null) {
config.addDataSourceProperty("user", dataSourceProperties.getUsername());
} else {
config.addDataSourceProperty("user", ""); // HikariCP doesn't allow null user
}
if (dataSourceProperties.getPassword() != null) {
config.addDataSourceProperty("password", dataSourceProperties.getPassword());
} else {
config.addDataSourceProperty("password", ""); // HikariCP doesn't allow null password
}
//MySQL optimizations, see https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(dataSourceProperties.getDriverClassName())) {
config.addDataSourceProperty("cachePrepStmts", nileRegistrarProperties.getDatasource().isCachePrepStmts());
config.addDataSourceProperty("prepStmtCacheSize", nileRegistrarProperties.getDatasource().getPrepStmtCacheSize());
config.addDataSourceProperty("prepStmtCacheSqlLimit", nileRegistrarProperties.getDatasource().getPrepStmtCacheSqlLimit());
}
if (metricRegistry != null) {
config.setMetricRegistry(metricRegistry);
}
return new HikariDataSource(config);
}
@Bean(destroyMethod = "close")
public DataSource liquibaseDataSource(DataSourceProperties dataSourceProperties, LiquibaseProperties liquibaseProperties) {
log.debug("Configuring Liquibase Datasource");
if (dataSourceProperties.getUrl() == null) {
log.error("Your database connection pool configuration is incorrect! The application" +
" cannot start. Please check your Spring profile, current profiles are: {}",
Arrays.toString(env.getActiveProfiles()));
throw new ApplicationContextException("Database connection pool is not configured correctly");
}
HikariConfig config = new HikariConfig();
config.setDriverClassName(dataSourceProperties.getDriverClassName());
config.setJdbcUrl(dataSourceProperties.getUrl());
// Close all connections after liquibase operation is complete.
config.setMinimumIdle(0);
if (dataSourceProperties.getUsername() != null) {
config.addDataSourceProperty("user", liquibaseProperties.getUser());
} else {
config.addDataSourceProperty("user", ""); // HikariCP doesn't allow null user
}
if (dataSourceProperties.getPassword() != null) {
config.addDataSourceProperty("password", liquibaseProperties.getPassword());
} else {
config.addDataSourceProperty("password", ""); // HikariCP doesn't allow null password
}
return new HikariDataSource(config);
}
@Bean
public SpringLiquibase liquibase(@Qualifier("liquibaseDataSource") DataSource dataSource,
LiquibaseProperties liquibaseProperties) {
// Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously
SpringLiquibase liquibase = new AsyncSpringLiquibase();
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:config/liquibase/master.xml");
liquibase.setContexts(liquibaseProperties.getContexts());
liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
liquibase.setDropFirst(liquibaseProperties.isDropFirst());
liquibase.setShouldRun(liquibaseProperties.isEnabled());
log.debug("Configuring Liquibase");
return liquibase;
}
}
Following is the error message.
Error creating bean with name 'dataSourceInitializer': Invocation of init method failed;
When I use single data source, everything is working is as expected.
Solution
I am working on a similar project, where I have two datasources. Here is what I do,
datasource.primary.url=jdbc:mysql://localhost:3306/databaseName
datasource.primary.username=username
datasource.primary.password=password
Similarly for second datasource,
datasource.secondary.url=jdbc:mysql://localhost:3306/databaseName
datasource.secondary.username=username
datasource.secondary.password=password
And in config class do just,
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDatasource")
@ConfigurationProperties(prefix = "datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
Wherever I need a datasource, I just autowire it like:
public void setJdbcTemplate(@Qualifier("secondaryDatasource") DataSource dataSource) {
this.jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
Note: The datasource annotated @Primary
will be injected if @Qualifier
is not used while autowiring.
Testing is much simpler with this. If you want to use in memory database for testing (most common technique), just provide another set of properties, with url of the in memory database, which can be used by the test class
Answered By - Rahul Sharma
Answer Checked By - Gilberto Lyons (JavaFixing Admin)