Table are not created in Spring batch testing with multiple datasource - spring-boot

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

Related

Spring Boot, required a single bean, but 2 were found when creating multiple datasources

I have an application in Spring Boot 1.4 which I'm trying to add additional datasources to.
First I setup a primary datasource and ran the application to check it still worked, and it did. Then I went ahead and added a second datasource, but when I did that I got the following error;
Description:
Field userRepo in com.nationallocums.config.CustomUserDetailsService required a single bean, but 2 were found:
- nlDoctorsEntityManager: defined by method 'nlDoctorsEntityManager' in class path resource [com/nationallocums/config/NLDoctorsDataSourceConfiguration.class]
- primaryEntityManager: defined by method 'primaryEntityManager' in class path resource [com/nationallocums/config/PrimaryDataSourceConfiguration.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
I don't understand why I'm seeing this error, as I've clearly marked one of the datasources with #Primary, but it seems Spring Boot isn't picking that up.
Here's my two datasource configurations;
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManager",
transactionManagerRef = "primaryTransactionManager",
basePackages = { "com.nationallocums.repository" })
#EnableTransactionManagement
public class PrimaryDataSourceConfiguration {
#Bean(name = "primaryDataSource")
#ConfigurationProperties(prefix = "spring.datasource")
#Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "primaryEntityManager")
#Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManager(final EntityManagerFactoryBuilder builder, #Qualifier("primaryDataSource") final DataSource dataSource) {
final Map<String, String> properties = new HashMap<>();
return builder
.dataSource(dataSource)
.properties(properties)
.packages("com.nationallocums.model")
.persistenceUnit("primary")
.build();
}
#Bean(name = "primaryTransactionManager")
#Primary
public PlatformTransactionManager nlDoctorsTransactionManager(#Qualifier("primaryEntityManager") final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
and...
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "nlDoctorsEntityManager",
transactionManagerRef = "nlDoctorsTransactionManager",
basePackages = { "com.nationallocums.eclipse.nldoctorsrepository" })
#EnableTransactionManagement
public class NLDoctorsDataSourceConfiguration {
#Bean(name = "nlDoctorsDataSource")
#ConfigurationProperties(prefix = "spring.nldoctors-datasource")
public DataSource nlDoctorsDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "nlDoctorsEntityManager")
public LocalContainerEntityManagerFactoryBean nlDoctorsEntityManager(final EntityManagerFactoryBuilder builder, #Qualifier("nlDoctorsDataSource") final DataSource dataSource) {
final Map<String, String> properties = new HashMap<>();
return builder
.dataSource(dataSource)
.properties(properties)
.packages("com.nationallocums.eclipse.model")
.persistenceUnit("nlDoctors")
.build();
}
#Bean(name = "nlDoctorsTransactionManager")
public PlatformTransactionManager nlDoctorsTransactionManager(#Qualifier("nlDoctorsEntityManager") final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Can anyone spot what I've done wrong?
I managed to fix this by changing my repository from...
#PersistenceContext
private EntityManager entityManager;
to...
#Autowired
#Qualifier("primaryDataSource")
private EntityManager entityManager;

Spring-boot TransactionRequiredException: Executing an update/delete query

My service method is annotated with Transnational (org.springframework.transaction.annotation)
But still a "TransactionRequiredException" occurs when making an update on table Persons.
A select on the same table works fine.
#Transactional
public String myMethod(String contractNo){
myRepository.resetValues(contractNo);
}
#Repository
public interface MyRepository extends JpaRepository<Persons, Long> {
#Modifying
#Query(value = "UPDATE PERSONS SET status = 0 WHERE LOGIN_NAME like :contractNo", nativeQuery = true)
void resetValues(#Param("contractNo") String contractNo);
}
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
basePackages = { "com.mypackage.repositories" }
)
public class EBankingDBConfig {
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "spring.eba-datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSource") DataSource dataSource) {
return builder.dataSource(dataSource)
.packages("com.mypackage.model")
.persistenceUnit("myPersistenceUnit")
.build();
}
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Because there are multiple TransactionManagers beans defined in the project, you have to annotate transactions of non-primary TransactionaManagers with their names. For example, in my config above, the TransactionManager name is defined as "transactionManager".
So, the Transactional annotation on methods should look line this : #Transactional("transactionManager")

Spring JPA - Single Repository class with multiple JNDIs

I am using Spring JPA Repository to connect to Oracle.
My Repository package is com.demo.infrastructure.repository;
Repository class is StoreRepo.java
#Repository
public interface StoreRepo extends JpaRepository<StoreAttribute, String> {
#Query("select storeAttributeName from StoreAttribute order by storeAttributeName asc")
List<String> fetchAllStoreAttributeNames();
List<StoreAttribute> findAllByOrderByStoreAttributeNameAsc();
}
Problem:
I am using JNDI config to configure data source. Currently it has only one JNDI entry. Now I want to use two user names for the same database, one with admin(read-write) access and the other with user(read-only) access. Both these users will access the same Repository and same entity.
I tried the solutions already available which uses two different repository packages for each data source. But I want the Repository "StoreRepo" to be the same.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryAdmin",
basePackages = { "com.demo.infrastructure.repository" }
)
public class DataSourceAdminConfig {
#Primary
#Bean(name = "dataSourceAdmin")
public DataSource dataSource() {
return new JndiDataSourceLookup().getDataSource("jdbc/myds_admin");
}
#Primary
#Bean(name = "entityManagerFactoryAdmin")
public LocalContainerEntityManagerFactoryBean
entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSourceAdmin") DataSource dataSource
) {
return builder.dataSource(dataSource).
packages("com.demo.domain.model.entities").
persistenceUnit("read-write").
build();
}
#Primary
#Bean(name = "transactionManagerAdmin")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactoryAdmin") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
I should have two classes like this with different package (Refer basePackages). But I dont want this solution instead want to use single repository package and the same repository class.
Solution that worked for me.
1) Created separate config classes one for admin user and app user each
2) Created seprate entity manager references one for admin user and app user each
3) Instantiated the same Repositoy class (without using #Repository annotation) through java code and using respective entity manager
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryAdmin"
)
public class AdminUserConfig {
#Primary
#Bean(name = "dataSourceAdmin")
public DataSource dataSourceAdmin(#Value("${spring.datasource.admin-user.jndi-name}") String key) {
return new JndiDataSourceLookup().getDataSource(key);
}
#Primary
#Bean(name = "entityManagerFactoryAdmin")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryAdmin(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSourceAdmin") DataSource dataSource
) {
return builder.dataSource(dataSource).
packages("com.demo.domain.model.entities").
persistenceUnit("read-write").
build();
}
#Bean(name = "entityManagerAdmin")
public EntityManager entityManagerAdmin(#Qualifier("entityManagerFactoryAdmin") EntityManagerFactory
entityManagerFactory) {
return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
}
#Bean(name = "adminRepository")
public StoreRepo readWriteDimStoreRepository(#Qualifier("jpaRepositoryFactoryAdmin")
JpaRepositoryFactory repositoryFactory) {
return repositoryFactory.getRepository(StoreRepo.class);
}
}
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryApp"
)
public class AppUserConfig {
#Primary
#Bean(name = "dataSourceApp")
public DataSource dataSourceApp(#Value("${spring.datasource.App-user.jndi-name}") String key) {
return new JndiDataSourceLookup().getDataSource(key);
}
#Primary
#Bean(name = "entityManagerFactoryApp")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryApp(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSourceApp") DataSource dataSource
) {
return builder.dataSource(dataSource).
packages("com.demo.domain.model.entities").
persistenceUnit("read-only").
build();
}
#Bean(name = "entityManagerAdmin")
public EntityManager entityManagerApp(#Qualifier("entityManagerFactoryApp") EntityManagerFactory
entityManagerFactory) {
return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
}
#Bean(name = "AppRepository")
public StoreRepo readOnlyStoreRepository(#Qualifier("jpaRepositoryFactoryApp")
JpaRepositoryFactory repositoryFactory) {
return repositoryFactory.getRepository(StoreRepo.class);
}
}
//#Repository
public interface StoreRepo extends JpaRepository<StoreAttribute, String> {
#Query("select storeAttributeName from StoreAttribute order by
storeAttributeName asc")
List<String> fetchAllStoreAttributeNames();
List<StoreAttribute> findAllByOrderByStoreAttributeNameAsc();
}

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.

SpringBoot - Multimodule projet & datasources - Unable to scan #Entity.class

I have some trouble to configure my multimodule projet with multiple datasources. So, I need your help.
I have allready try :
Springboot multimodule project
SpringBoot ComponentScan issue with multi-module project
SpringBoot scanBasePackages not working in multi module project
And https://mhdevelopment.wordpress.com/2014/09/16/how-to-configure-spring-boot-with-jpa-and-multiple-projects/
But nothing work, i have the same issue.
So, that m'y multi-module classpath:
BusinessModule, that include repositories and Entities:
base package : com.mycompany.business.
In that module, i have an external dependency where we can found #Entity class ( package : com.mycompany.model)
RestModule, that are used to use BusinessModule.
base package : com.mycompany.rest
Business configuration
Configuration for datasources
First data source:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory",
basePackages = {
"com.mycompany.business.repository", "com.mycompany.model" })
#PropertySource(value =
"file:${CPROCESS_INSTALL}/server/custom_config/datasources.properties")
public class BusinessDbConfig {
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "business.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("dataSource") DataSource dataSource) {
return builder.dataSource(dataSource)
.packages("com.mycompany.business.model", "com.mycompany.model")
.persistenceUnit("business").build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Second data source:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "barEntityManagerFactory", transactionManagerRef = "barTransactionManager", basePackages = {
"com.mycompany.bonita.repository" })
#PropertySource(value = "file:${CPROCESS_INSTALL}/server/custom_config/datasources.properties")
public class BonitaDbConfig {
#Bean(name = "barDataSource")
#ConfigurationProperties(prefix = "bonita.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "barEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("barDataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.mycompany.bonita.model")
.persistenceUnit("bar").build();
}
#Bean(name = "barTransactionManager")
public PlatformTransactionManager barTransactionManager(
#Qualifier("barEntityManagerFactory") EntityManagerFactory barEntityManagerFactory) {
return new JpaTransactionManager(barEntityManagerFactory);
}
}
Repository configuration
#RepositoryRestResource
public interface CDocumentRepository extends JpaRepository<CDocument, Long> {
#Query("select dp from CDocument d where d.persistenceid = ?1")
public CDocument findByPersistenceId(Long persistenceId);
}
Entity
#javax.persistence.Entity(name = "CDocument")
#Table(name = "CDOCUMENT")
public class CDocument{ ... }
Rest module configuration
App configuration
#SpringBootApplication(scanBasePackages= {"com.mycompany"})
#EnableJpaRepositories("com.mycompany")
#EntityScan("com.mycompany")
#Import({ BonitaDbConfig.class, BusinessDbConfig.class })
public class RestApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(RestApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(RestApplication.class, args);
}
}
My controller
#RestController
public class CreateSpaceController {
private CDocushareConnectRepository cDocushareConnectRepository;
private CDocumentRepository cDocumentRepository;
private DocumentRepository documentRepository;
private CustomtableRepository customTableRepository;
private CFieldsMappingRepository cFieldsMappingRepository;
public CreateSpaceController(CDocumentRepository cDocumentRepository) {
super();
this.cDocumentRepository = cDocumentRepository;
}
...
}
When i try to start test or app, i have this error:
java.lang.IllegalArgumentException: Not a managed type: class com.mycompany.model.CDocument
I don't know why... Any help ?

Resources