Spring boot two data sources, unit tests stopped working - spring

I have switched my application to use two data sources, the code runs fine and both are picked up, however my unit tests have started to fail, code is below, help much appreciated.
Caused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
at org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.getDriverClassName(DataSourceProperties.java:180)
at org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$NonEmbeddedConfiguration.dataSource(DataSourceAutoConfiguration.java:120)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
... 71 more
application.properties (in main and in test folders):
datasource.primary.url = <url>
datasource.primary.username = <user>
datasource.primary.password = <password>
datasource.secondary.url = <url>
datasource.secondary.username = <user>
datasource.secondary.password = <pass>
Main program:
#EnableAutoConfiguration
#Configuration
#EntityScan({"com.example.domain","com.example.common.domain"})
#PropertySource(value = "classpath:application.properties")
#EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Primary Data source config:
#Configuration
#EnableJpaRepositories(basePackages = "com.example.repository",
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager")
#EnableTransactionManagement
public class PrimaryConfiguration {
#Bean
#ConfigurationProperties(prefix = "datasource.primary")
#Primary
public DataSource primaryDataSource()
{
return DataSourceBuilder.create().build();
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(final EntityManagerFactoryBuilder builder)
{
return builder
.dataSource(primaryDataSource())
.packages("uk.gov.dwp.pss.roc.domain")
.persistenceUnit("primaryPersistenceUnit")
.build();
}
#Bean
#Primary
public JpaTransactionManager primaryTransactionManager(#Qualifier("primaryEntityManagerFactory") final EntityManagerFactory factory)
{
return new JpaTransactionManager(factory);
}
}
Secondary config class:
#Configuration
#EnableJpaRepositories(basePackages = "com.example.common.repository",
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager")
#EnableTransactionManagement
public class SecondaryConfiguration {
#Bean
#ConfigurationProperties(prefix = "datasource.secondary")
public DataSource secondaryDataSource()
{
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(final EntityManagerFactoryBuilder builder)
{
return builder
.dataSource(secondaryDataSource())
.packages("uk.gov.dwp.pss.commons.domain.security")
.persistenceUnit("secondaryPersistenceUnit")
.build();
}
#Bean
public JpaTransactionManager secondaryTransactionManager(#Qualifier("secondaryEntityManagerFactory") final EntityManagerFactory factory)
{
return new JpaTransactionManager(factory);
}
}
Repository class:
public interface MyRepository extends JpaRepository<MyObject, String>,JpaSpecificationExecutor<MyObject> {
}
Unit test class use annotation:
#SpringApplicationConfiguration(classes = MyApplication.class)

A database driver was not found on the classpath for the AutoConfiguration to setup while running your test cases.

As #ndrone stated you need to set your database driver. It is unclear if you are attempting to use AutoConfigureTestDatabase. If so you should reference AutoConfigureTestDatabase from the Spring Boot docs.
If this is the case you could specify the Embedded database to use by using the following in your test (configuration):
#AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
Otherwise, you could modify your application.properties in your test folder to specify the connection type such as:
datasource.primary.driver-class-name=org.h2.Driver
datasource.secondary.driver-class-name=org.h2.Driver

Related

Table are not created in Spring batch testing with multiple datasource

I have added an additional datasource in Spring batch project.
When I am trying to run the job locally its running successfully I am having issue on running Integration testcases.
I have data.sql script which inserting data into table. It does not have any sql script to create table as I have added in test properties "spring.jpa.hibernate.ddl-auto=create-drop"
test/resources/application.properties
first.datasource.jdbc-url=jdbc:h2:mem:testdb
first.datasource.driverClassName=org.h2.Driver
first.datasource.username=username
first.datasource.password=password
first.datasource.hikari.connectionTimeout=400000
first.datasource.hikari.maximum-pool-size=600000
first.datasource.hikari.minimum-idle=5
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.jdbc.batch_size=10
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.batch_versioned_data=true
spring.batch.initialize-schema=always
spring.batch.job.enabled=false
second.datasource.jdbc-url=jdbc:h2:mem:testdb
second.datasource.driverClassName=org.h2.Driver
second.datasource.username=username
second.datasource.password=password
second.datasource.hikari.connectionTimeout=400000
second.datasource.hikari.maximum-pool-size=600000
second.datasource.hikari.minimum-idle=5
Configuration file for first Datasource is
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "firstappEntityManagerFactory",
transactionManagerRef = "firstappTransactionManager",
basePackages = "com.app.repository"
)
#EnableTransactionManagement
public class firsDataSourceDBConfig {
#Bean(name="firstappDataSource")
#Primary
#ConfigurationProperties(prefix = "first.datasource")
public DataSource appDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "firstappEntityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean appEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("firstappDataSource") DataSource appDataSource){
return builder
.dataSource(appDataSource)
.packages("com.app.domain")
.persistenceUnit("firstUnit")
.build();
}
#Bean(name = "firstappTransactionManager")
#Primary
public PlatformTransactionManager appTransactionManager(#Qualifier("firstappEntityManagerFactory") EntityManagerFactory
appEntityManagerFactory) {
return new JpaTransactionManager(appEntityManagerFactory);
}
#Bean
public BatchConfigurer configurer(#Qualifier("firstappEntityManagerFactory") EntityManagerFactory appEntityManagerFactory) {
return new JpaTransactionManager(appEntityManagerFactory);
}
}
Second one is without BatchConfigurer beans
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "secondappEntityManagerFactory",
transactionManagerRef = "secondappTransactionManager",
basePackages = "com.abc.repository"
)
#EnableTransactionManagement
public class firsDataSourceDBConfig {
#Bean(name="secondappDataSource")
#Primary
#ConfigurationProperties(prefix = "second.datasource")
public DataSource appDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "secondappEntityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean appEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("secondappDataSource") DataSource appDataSource){
return builder
.dataSource(appDataSource)
.packages("com.app.domain")
.persistenceUnit("secondUnit")
.build();
}
#Bean(name = "secondappTransactionManager")
#Primary
public PlatformTransactionManager appTransactionManager(#Qualifier("secondappEntityManagerFactory") EntityManagerFactory
appEntityManagerFactory) {
return new JpaTransactionManager(appEntityManagerFactory);
}
}
}
Files with annotation #DataJpaTest are running successfully
#RunWith(SpringRunner.class)
#DataJpaTest
public class RepositoryTest {
#Autowired
private FirstDataSourceRepository repo;
..........
}
Test cases which are annotated with #SpringBootTest are failing
#RunWith(SpringRunner.class)
#SpringBootTest
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public class SpringBatchIntegrationTest {
#Autowired
private FirstDataSourceRepository repo;
..........
}
Error stating BeanCreation Exception
firstappEntityManagerFactory.....org.h2.jdbc.JdbcSQLException: Table "TESTING" not found

How to set EntityManagerFactory name on a Spring JPA Repository?

I have multiple database connections with named EntityManagers:
#Bean(name = "integDB")
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "integEM")
public LocalContainerEntityManagerFactoryBean integDbEntityManagerFactory(EntityManagerFactoryBuilder builder) {
....
}
How do I specify which entity manager to use here?
#Repository
public interface IntControleFilaRepository extends JpaRepository<IntControleFilaEntity, String> {
}
Spring is complaining about it:
Parameter 0 of constructor in ... required a bean named 'entityManagerFactory' that could not be found.
Create a configuration class for each database and specify the entityManager associated with each one:
#Configuration
#EnableJpaRepositories(basePackages = {"..."}, entityManagerFactoryRef = "integEM", transactionManagerRef = "integTM")
public class IntegReposConfig {
}

problem with writing to 2 databases from Spring boot

I was trying this code from github:https://github.com/kodinor/spring-data-many-dbs
It's an example of how to use multiple db's from a Spring-boot application. it worked fine but I added the Spring-boot-starter-web dependency and now I'm getting an error:
>Method requestMappingHandlerMapping in >org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$En>ableWebMvcConfiguration required a single bean, but 2 were found:
> - productDSEmFactory: defined by method 'productDSEmFactory' in class >path resource [com/kodinor/configuration/ProductDBConfiguration.class]
> - userDSEmFactory: defined by method 'userDSEmFactory' in class path >resource [com/kodinor/configuration/UserDBConfiguration.class]
>
>
>Action:
>
>Consider marking one of the beans as #Primary, updating the consumer to >accept multiple beans, or using #Qualifier to identify the bean that should >be consumed<br>
I have two config files:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackageClasses = UserRepository.class, entityManagerFactoryRef = "userDSEmFactory", transactionManagerRef = "userDSTransactionManager")
public class UserDBConfiguration {
#Primary
#Bean
#ConfigurationProperties("spring.datasource1")
public DataSourceProperties userDSProperties() {
return new DataSourceProperties();
}
#Primary
#Bean
public DataSource userDS(#Qualifier("userDSProperties") DataSourceProperties userDSProperties) {
return userDSProperties.initializeDataSourceBuilder().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean userDSEmFactory(#Qualifier("userDS") DataSource userDS, EntityManagerFactoryBuilder builder) {
return builder.dataSource(userDS).packages(User.class).build();
}
#Primary
#Bean
public PlatformTransactionManager userDSTransactionManager(EntityManagerFactory userDSEmFactory) {
return new JpaTransactionManager(userDSEmFactory);
}
}
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackageClasses = ProductRepository.class, entityManagerFactoryRef = "productDSEmFactory", transactionManagerRef = "productDSTransactionManager")
public class ProductDBConfiguration {
#Bean
#ConfigurationProperties("spring.datasource2")
public DataSourceProperties productDSProperties() {
return new DataSourceProperties();
}
#Bean
public DataSource productDS(#Qualifier("productDSProperties") DataSourceProperties productDSProperties) {
return productDSProperties.initializeDataSourceBuilder().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean productDSEmFactory(#Qualifier("productDS") DataSource productDS, EntityManagerFactoryBuilder builder) {
return builder.dataSource(productDS).packages(Product.class).build();
}
#Bean
public PlatformTransactionManager productDSTransactionManager(EntityManagerFactory productDSEmFactory) {
return new JpaTransactionManager(productDSEmFactory);
}
}
Application.properties:
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=true
spring.datasource1.url=jdbc:mysql://localhost:3306/userdb?autoReconnect=true&useSSL=false
spring.datasource1.username=user
spring.datasource1.password=pass
spring.datasource2.url=jdbc:mysql://localhost:3306/productdb?autoReconnect=true&useSSL=false
spring.datasource2.username=user
spring.datasource2.password=pass
And a simple main app that adds some init data:
#SpringBootApplication
public class SpringDataManyDbsApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDataManyDbsApplication.class, args);
}
#Autowired
private UserRepository userRepository;
#Autowired
private ProductRepository productRepository;
#PostConstruct
void init () {
User user = new User("john", "doe");
userRepository.save(user);
Product product = new Product("chair", 5);
productRepository.save(product);
}
}
I tried to add the #Primary annotation to userDSEmFactory. The error goes away but the product data isn't saved anymore. Any ideas how to save this problem? I don't have a lot of experience with Spring-boot and I've read dozens of articles but many seem to do things in a different way. Thanks so much for helping me out!
update
I've added the #Primary annotation like this:
#Primary
#Bean
public LocalContainerEntityManagerFactoryBean userDSEmFactory(#Qualifier("userDS") DataSource userDS, EntityManagerFactoryBuilder builder) {
return builder.dataSource(userDS).packages(User.class).build();
}
No there are no more errors, but only the user is being saved in the primary db and nothing is being saved to the product (second) database.
So if anybody has suggestions what could be the cause of that..
You are missing #Primary annotation on your userDSEmFactory
#Primary
#Bean
public LocalContainerEntityManagerFactoryBean userDSEmFactory(#Qualifier("userDS") DataSource userDS, EntityManagerFactoryBuilder builder) {
return builder.dataSource(userDS).packages(User.class).build();
}
And also it is goot to use once #EnableTransactionManagement at your main class.

Spring boot manual config not scanning Repository objects?

Despite some fairly clear examples, I'm failing to get a manual configuration of a spring boot application to work. I usually allow it to configure right from the application.properties file but the requirement demands that I use multiple data sources. And, yes, I have tried to recreate the Baeldung example (https://www.baeldung.com/spring-data-jpa-multiple-databases) as recommended by the posts I've seen here. Here is my config:
#Configuration
#EnableJpaRepositories(basePackages = "com.ezcorp.costumerrefresh.db.sqlserver", entityManagerFactoryRef = "sqlserverEntityManagerFactory", transactionManagerRef = "sqlserverTransactionManager")
#EnableTransactionManagement
public class SqlServerConfig {
#Bean(name="sqlserverDataSource")
#ConfigurationProperties(prefix = "spring.datasource")
#Primary
public DataSource sqlserverDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
dataSource.setUrl("jdbc:sqlserver://aswn-tbd-db01:1433;databaseName=DE_RTDS");
dataSource.setUsername("cs_user");
dataSource.setPassword("cs_user");
return dataSource;
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean sqlserverEntityManagerFactory(final EntityManagerFactoryBuilder builder) {
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.dialect", "org.hibernate.dialect.SQLServer2008Dialect");
return builder
.dataSource(sqlserverDataSource())
.properties(properties)
.packages("com.ezcorp.costumerrefresh.domain.sqlserver")
.persistenceUnit("adminPersistenceUnit")
.build();
}
#Bean
#Primary
public JpaTransactionManager sqlserverTransactionManager(#Qualifier("sqlserverEntityManagerFactory") final EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
}
Service:
#Service
public class CustomerRefreshService {
#Autowired
private ExistingLoanModelDataRepository existingLoanModelDataRepository;
public void execute() {
System.out.println("show me");
}
}
Repository:
#Repository
public interface ExistingLoanModelDataRepository extends CrudRepository<ExistingLoanModelData, Long> {
ExistingLoanModelData findByCustomerId(Long customerId);
}
error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.ezcorp.customerrefresh.db.sqlserver.ExistingLoanModelDataRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
...
What am I doing wrong? This is about as straight-forward as it gets. I've eliminated the other (mongo) config setup to simplify. This has to be an obvious one...just not to me :-)
Looks like a typo.
#EnableJpaRepositories(basePackages = "com.ezcorp.costumerrefresh.db.sqlserver", entityManagerFactoryRef = "sqlserverEntityManagerFactory", transactionManagerRef = "sqlserverTransactionManager")

Spring boot test wrong configuration classes

I am trying to test my repository layer using Spring Boot 2.0.1 but when I run my test class, Spring tries to instantiate a Config class not from the test package.
Here is the test code:
TestConfig.class
#Configuration
#Import(value = {TestDatabaseConfig.class})
#Profile("local")
public class TestConfig {
}
TestDatabaseConfig.class
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "logEntityManagerFactory",
transactionManagerRef = "logTransactionManager",
basePackages = { "it.xxx.yyy.repository.log" })
#EntityScan(basePackages = {"it.xxx.yyy.model.log", "it.xxx.yyy.common"})
#Profile("local")
public class TestDatabaseConfig {
#Bean("logDataSourceProperties")
public DataSourceProperties logDataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "logDataSource")
public DataSource dataSource(#Qualifier("logDataSourceProperties") DataSourceProperties properties) {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.build();
}
#Bean(name = "logEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean logEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("logDataSource") DataSource logDataSource) {
return builder.dataSource(logDataSource)
.packages("it.xxx.model.log")
.persistenceUnit("log")
.build();
}
#Bean(name = "logTransactionManager")
public PlatformTransactionManager logTransactionManager(#Qualifier("logEntityManagerFactory")EntityManagerFactory logEntityManagerFactory) {
return new JpaTransactionManager(logEntityManagerFactory);
}
}
When I run this class
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("local")
public class LogRepositoryTest {
#Autowired
private ResultLogRepository resultLogRepository;
#Test
public void init(){
}
}
it says :
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'kafkaProducer': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'kafka.topic.operation' in value "${kafka.topic.operation}"
[...]
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'kafka.topic.operation' in value "${kafka.topic.operation}"
But I cannot understand why it brings up my KafkaProducer.class from my main package (that has #Configuration annotation on it).
In your LogRepositoryTest test class you should indicate the alternate test configuration class that should be taken into account, in your case I think should be the TestConfig.
From Spring Boot documentation:
If you are familiar with the Spring Test Framework, you may be used to using #ContextConfiguration(classes=…​) in order to specify which Spring #Configuration to load. Alternatively, you might have often used nested #Configuration classes within your test.
So annotate LogRepositoryTest with #ContextConfiguration(classes = {TestConfig.class})
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("local")
#ContextConfiguration(classes = {TestConfig.class})
public class LogRepositoryTest {
#Autowired
private ResultLogRepository resultLogRepository;
#Test
public void init(){
}
}
UPDATE
Also annotate your configuration class with:
#EnableAutoConfiguration
Something like:
#Configuration
#EnableAutoConfiguration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "logEntityManagerFactory",
transactionManagerRef = "logTransactionManager",
basePackages = { "it.xxx.yyy.repository.log" })
#EntityScan(basePackages = {"it.xxx.yyy.model.log", "it.xxx.yyy.common"})
#Profile("local")
public class TestDatabaseConfig {
//...
}
UPDATE 2
For error:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.autoconfigure.jdbc.DataSourceProperties' available: expected single matching bean but found 2: logDataSourceProperties,spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
Completely remove the method:
#Bean("logDataSourceProperties")
public DataSourceProperties logDataSourceProperties() {
return new DataSourceProperties();
}
and change your:
#Bean(name = "logDataSource")
public DataSource dataSource(#Qualifier("logDataSourceProperties") DataSourceProperties properties) {
// ...
}
to:
#Bean(name = "logDataSource")
public DataSource dataSource(DataSourceProperties properties) {
// ...
}

Resources