Spring Boot Jpa Batch - CannotCreateTransactionException - spring-boot

I have a Spring Boot + JPA + Spring batch (+Spring Integration) project. Also there's Flyway, configured apart.
I have configured spring batch to use a different datasource than default "spring.jpa.datasource". This is fine.
Inside my tasklet I query on two datasources, first query on ds1 goes right, second query on ds2 goes wrong:
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder#2fb082ff] for key [HikariDataSource (HikariPool-1)] bound to thread [main]
More details:
I have a configuration class for each datasource, annotated with
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories
In each configuration a bean for DataSource, LocalContainerEntityManagerFactoryBean and JpaTransactionManager is defined, with custom name.
A service is defined for each datasource as:
#Service #Transactional(transactionManager=JpaApsConfiguration.APS_TRANSACTION_MANAGER, propagation = Propagation.REQUIRES_NEW)
and a entitymanager is autowired using the right qualifier
(note: query made through JpaRepository classes)
Any suggestion on what could cause this behaviour? Thanks
Note:
Database connects successfully
I had a different error before
(already fixed, but maybe could be helpful):
'NoUniqueBeanDefinitionException' as Spring couldn't identify the
correct 'PlatformTransactionManager' between my two transaction
manager beans and the default transactionManager.
EDIT:
Here the complete stacktrace for the error:
2018-05-08 12:12:24 INFO o.s.batch.core.job.SimpleStepHandler - Executing step: [STEP#PROCESS_SHIPMENTS]
2018-05-08 12:12:24 ERROR o.s.batch.core.step.AbstractStep - Encountered an error executing step STEP#PROCESS_SHIPMENTS in job PROCESS_APS
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder#351e89fc] for key [HikariDataSource (HikariPool-1)] bound to thread [main]
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:450)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:378)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:474)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
at dkr.astreconnector.service.GespeService$$EnhancerBySpringCGLIB$$73dbb368.getValueDetailByUniq()
at dkr.astreconnector.batch.worker.ShipmentProcessor.process(ShipmentProcessor.java:63)
at dkr.astreconnector.batch.worker.ShipmentProcessor.process(ShipmentProcessor.java:30)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:126)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:303)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:202)
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:272)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:81)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145)
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:200)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:66)
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67)
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169)
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144)
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:136)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:308)
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:141)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:134)
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.execute(JobLauncherCommandLineRunner.java:163)
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.executeLocalJobs(JobLauncherCommandLineRunner.java:179)
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.launchJobFromProperties(JobLauncherCommandLineRunner.java:134)
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.run(JobLauncherCommandLineRunner.java:128)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:790)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
at dkr.astreconnector.AstreConnectorBatchApplication.main(AstreConnectorBatchApplication.java:14)
Caused by: java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder#351e89fc] for key [HikariDataSource (HikariPool-1)] bound to thread [main]
at org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:193)
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:425)
... 43 common frames omitted
2018-05-08 12:12:24 INFO o.s.b.c.l.support.SimpleJobLauncher - Job: [FlowJob: [name=PROCESS_APS]] completed with the following parameters: [{run.id=128}] and the following status: [FAILED]

A little bit late, but I'd like to reply to #mad_fox
Primary db:
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = JpaAppsConfiguration.APPS_ENTITY_MANAGER,
transactionManagerRef = "transactionManager",
basePackages = { JpaAppsConfiguration.APPS_REPOSITORIES })
#EnableAutoConfiguration
public class JpaAppsConfiguration {
public final static String APPS_PERSISTENCE_UNIT = "apps";
public final static String APPS_DATA_SOURCE = "appDs";
public final static String APPS_ENTITY_MANAGER = "appsEmf";
public final static String APPS_TRANSACTION_MANAGER = "appsTm";
protected final static String APPS_PACKAGES = ""; //my models package
protected final static String APPS_REPOSITORIES = ""; //my repos package
#Primary
#Bean(name = APPS_DATA_SOURCE)
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource appsDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = APPS_ENTITY_MANAGER)
#PersistenceContext(unitName = APPS_PERSISTENCE_UNIT)
public LocalContainerEntityManagerFactoryBean appsEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier(APPS_DATA_SOURCE) DataSource appsDataSource) {
LocalContainerEntityManagerFactoryBean emf = builder
.dataSource(appsDataSource)
.persistenceUnit(APPS_PERSISTENCE_UNIT)
.packages(APPS_PACKAGES)
.build();
// Vendor adapter
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(vendorAdapter);
return emf;
}
}
Secondary db:
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = JpaGepeConfiguration.GEPE_ENTITY_MANAGER,
basePackages = { JpaGepeConfiguration.GEPE_REPOSITORIES })
#EnableAutoConfiguration
public class JpaGepeConfiguration {
public static final String GEPE_PERSISTENCE_UNIT = "gn1";
public static final String GEPE_DATA_SOURCE = "gn1ds";
public static final String GEPE_ENTITY_MANAGER = "gn1emf";
public static final String GEPE_TRANSACTION_MANAGER = "gn1tm";
public static final String GEPE_PACKAGES = ""; //my models package
public static final String GEPE_REPOSITORIES = ""; //my repos package
#Bean(name = GEPE_DATA_SOURCE)
#ConfigurationProperties(prefix = "gn1.datasource")
public DataSource gepeDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = GEPE_ENTITY_MANAGER)
#PersistenceContext(unitName = GEPE_PERSISTENCE_UNIT)
public LocalContainerEntityManagerFactoryBean gepeEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier(GEPE_DATA_SOURCE) DataSource gepeDataSource) {
LocalContainerEntityManagerFactoryBean emf = builder
.dataSource(gepeDataSource)
.persistenceUnit(GEPE_PERSISTENCE_UNIT)
.packages(GEPE_PACKAGES)
.build();
// Vendor adapter
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(vendorAdapter);
return emf;
}
}
Methods in #Service classes for primary db are annotated as:
#Transactional(transactionManager = JpaAppsConfiguration.APPS_TRANSACTION_MANAGER, readOnly = false, propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
Parameters in properties file are as follows:
#DATABASE CONNECTION
#Primary
spring.datasource.username=
spring.datasource.password=
spring.datasource.driverClassName=
spring.datasource.url=j
spring.datasource.jdbcUrl=
spring.datasource.name=
#Secondary
gepe.datasource.username=
gepe.datasource.password=
gepe.datasource.driverClassName=
gepe.datasource.url=
gepe.datasource.jdbcUrl=
gepe.datasource.name=
Hope this helps.

Here is a code snippet for batch configuration. Make sure that you also override getTransactionManager method. (replace the value of qualifires with yours)
#Configuration
public class CustomBatchConfigurer extends DefaultBatchConfigurer {
#Autowired
#Qualifier("primaryDataSource")
private DataSource dataSource;
#Autowired
#Qualifier("primaryTransactionManager")
private PlatformTransactionManager transactionManager;
#Override
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
#Override
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factoryBean = new JobRepositoryFactoryBean();
factoryBean.setIsolationLevelForCreate("ISOLATION_REPEATABLE_READ");
factoryBean.setDataSource(dataSource);
factoryBean.setTransactionManager(transactionManager);
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
}

Related

One Database overrides the other when Primary Bean is defined on and vice versa

I have 2 JDBC Datasources defined in a Spring Boot application utilizing used in a Spring Batch job. However, after autowiring the datasources, only one gets used. The one used is the one annotated #Primary. If I place the annotation on the other JDBC datasource that gets used instead. In a nutshell only one of the JDBC datasources ever gets used. I use Lombok in some places but I'm unsure if that is playing a part.
Here are the datasources:
application.yml
symphony:
datasource:
driver-class-name: oracle.jdbc.OracleDriver
url: ...
type: com.zaxxer.hikari.HikariDataSource
username: <USR>
password: <PWD>
jndi-name: false
repo:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
username: sa
password: sa
jndi-name: false
Here is the first datasource:
#Configuration
public class RepoDbConfig {
#Bean
#ConfigurationProperties("repo.datasource")
public DataSourceProperties repoDataProperties() {
return new DataSourceProperties();
}
#Bean(name = "repoDataSource")
public DataSource dataSourcerepo() {
DataSource dataSource = repoDataProperties().initializeDataSourceBuilder().type(BasicDataSource.class)
.build();
return dataSource;
}
#Bean(name = "repoJdbcTemplate")
public JdbcTemplate repoJdbcTemplate(DataSource repoDataSource) {
return new JdbcTemplate(repoDataSource);
}
}
Here is the second datasource:
#Configuration
public class SymphonyDbConfig {
#Primary
#Bean
#ConfigurationProperties("symphony.datasource")
public DataSourceProperties symphonyDataSourceProperties() {
return new DataSourceProperties();
}
#Primary
#Bean(name = "symphonyDataSource")
public DataSource dataSourcesymphony() {
HikariDataSource dataSource = symphonyDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class)
.build();
return dataSource;
}
#Primary
#Bean(name = "symphonyJdbcTemplate")
public JdbcTemplate symphonyJdbcTemplate(DataSource symphonyDataSource) {
return new JdbcTemplate(symphonyDataSource);
}
}
The JobRepository beans are configured like this:
#Configuration
#RequiredArgsConstructor
public class JobRepositoryConfig {
final #Qualifier("repoDataSource")
DataSource repoDataSource;
#Bean("repoTransactionManager")
AbstractPlatformTransactionManager repoTransactionManager() {
return new ResourcelessTransactionManager();
}
#Bean("repoJobRepository")
public JobRepository repoJobRepository(DataSource repoDataSource) throws Exception {
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
jobRepositoryFactoryBean.setDataSource(repoDataSource);
jobRepositoryFactoryBean.setTransactionManager(repoTransactionManager());
jobRepositoryFactoryBean.setDatabaseType(DatabaseType.H2.getProductName());
return jobRepositoryFactoryBean.getObject();
}
#Bean("repoAppJobLauncher")
public JobLauncher careLocationAppJobLauncher(JobRepository repoJobRepository) {
SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
simpleJobLauncher.setJobRepository(repoJobRepository);
return simpleJobLauncher;
}
}
Finally the Batch Job beans used for the Job are configured here: The only part not shown is the launching of the job. All the required beans used are shown here:
#Configuration
#EnableBatchProcessing
#EnableScheduling
#RequiredArgsConstructor
#Slf4j
public class CellBatchConfig {
private final JobBuilderFactory jobBuilderFactory;
#Qualifier("repoAppJobLauncher")
private final JobLauncher repoAppJobLauncher;
private final StepBuilderFactory stepBuilderFactory;
#Value("${chunk-size}")
private int chunkSize;
#Qualifier("symphonyDataSource")
final DataSource symphonyDataSource;
#Qualifier("repoDataSource")
final DataSource symphonyDataSource;
#Bean
public JdbcPagingItemReader<CenterDto> cellItemReader(PagingQueryProvider pagingQueryProvider) {
return new JdbcPagingItemReaderBuilder<CenterDto>()
.name("cellItemReader")
.dataSource(symphonyDataSource)
.queryProvider(pagingQueryProvider)
.pageSize(chunkSize)
.rowMapper(new CellRowMapper())
.build();
}
#Bean
public PagingQueryProvider pagingQueryProvider() {
OraclePagingQueryProvider pagingQueryProvider = new OraclePagingQueryProvider();
final Map<String, Order> sortKeys = new HashMap<>();
sortKeys.put("ID", Order.ASCENDING);
pagingQueryProvider.setSortKeys(sortKeys);
pagingQueryProvider.setSelectClause(" ID, CELL_NO, MAT_VO ");
pagingQueryProvider.setFromClause(" from pvc.cells");
return pagingQueryProvider;
}
.......
}
The error results from only one of the datasources being used. That results that being used to query the Spring Batch job repository resulting in it failing: Here is the key portion of the stacktrace. It is trying to use the oracle datasource to query for the JobRespository resources and fails as a result:
Caused by: org.springframework.jdbc.BadSqlGrammarException:
PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from
BATCH_JOB_INSTANCE
where JOB_NAME = ? and JOB_KEY = ?]; nested exception is
java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist
In the class JobRepositoryConfig:
In the bean:
#Bean("symphonyJobRepository")
public JobRepository symphonyJobRepository(DataSource repoDataSource) throws Exception {
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
jobRepositoryFactoryBean.setDataSource(repoDataSource);
jobRepositoryFactoryBean.setTransactionManager(repoTransactionManager());
jobRepositoryFactoryBean.setDatabaseType(DatabaseType.H2.getProductName());
return jobRepositoryFactoryBean.getObject();
}
You didn't use the variable:
final #Qualifier("repoDataSource") DataSource repoDataSource;
So Spring uses a DataSource object which is annotated with #Primary annotation
I fixed it by making one bean the primary and also adding the qualifiers on the specific beans which had been missing, an omission on my part. For example, here I added the #Qualifier:
#Primary
#Bean(name = "symphonyDataSource")
#Qualifier("symphonyDataSource") // This was missing
public DataSource dataSourcesymphony() {
HikariDataSource dataSource = symphonyDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class)
.build();
return dataSource;
}

Couldn't determine Dialect for "oracle"

I am using Spring boot(2.3.5), Oracle19c DB, and Hibernate(5.4).
I tried to make multi-datasource connection, but I keep getting a dialect error Couldn't determine Dialect for "oracle".
Error creating bean with name 'jdbcDialect' defined in class path resource [org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfiguration$SpringBootJdbcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.relational.core.dialect.Dialect]: Factory method 'jdbcDialect' threw exception; nested exception is org.springframework.data.jdbc.repository.config.DialectResolver$NoDialectException: Cannot determine a dialect for org.springframework.jdbc.core.JdbcTemplate#2ba9ed19. Please provide a Dialect.
I basically followed this tutorial to configure multiple data sources.
application.properties:
spring.datasource-primary.username=oracleprimary
spring.datasource-primary.password=oracleprimary
spring.datasource-primary.url=jdbc:oracle:thin:#//localhost:1521/orcl
spring.datasource-secondary.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource-secondary.username=oraclesecondary
spring.datasource-secondary.password=oraclesecondary
spring.datasource-secondary.url=jdbc:oracle:thin:#//localhost:1521/orcl
Primary configuration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager",
basePackages = {"com.foo.primary.repository"})
public class PrimaryDataSourceConfiguration {
#Primary
#Bean(name = "primaryDataSourceProperties")
#ConfigurationProperties("spring.datasource-primary")
public DataSourceProperties primaryDataSourceProperties() {
return new DataSourceProperties();
}
#Primary
#Bean(name = "primaryDataSource")
#ConfigurationProperties("spring.datasource-primary.configuration")
public DataSource primaryDataSource(#Qualifier("primaryDataSourceProperties") DataSourceProperties primaryDataSourceProperties) {
return primaryDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Primary
#Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
EntityManagerFactoryBuilder primaryEntityManagerFactoryBuilder, #Qualifier("primaryDataSource") DataSource primaryDataSource) {
Map<String, String> primaryJpaProperties = new HashMap<>();
primaryJpaProperties.put("hibernate.dialect", "org.hibernate.dialect.Oracle12cDialect");
return primaryEntityManagerFactoryBuilder
.dataSource(primaryDataSource)
.packages("com.foo.primary.model")
.persistenceUnit("primaryDataSource")
.properties(primaryJpaProperties)
.build();
}
#Primary
#Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager(
#Qualifier("primaryEntityManagerFactory") EntityManagerFactory primaryEntityManagerFactory) {
return new JpaTransactionManager(primaryEntityManagerFactory);
}
Second configuration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager",
basePackages = {"com.foo.secondary.repository"})
public class SecondaryDataSourceConfiguration {
#Bean(name = "secondaryDataSourceProperties")
#ConfigurationProperties("spring.datasource-secondary")
public DataSourceProperties secondaryDataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "secondaryDataSource")
#ConfigurationProperties("spring.datasource-secondary.configuration")
public DataSource secondaryDataSource(#Qualifier("secondaryDataSourceProperties") DataSourceProperties secondaryDataSourceProperties) {
return secondaryDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
EntityManagerFactoryBuilder secondaryEntityManagerFactoryBuilder, #Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
Map<String, String> secondaryJpaProperties = new HashMap<>();
secondaryJpaProperties.put("hibernate.dialect", "org.hibernate.dialect.Oracle12cDialect");
return secondaryEntityManagerFactoryBuilder
.dataSource(secondaryDataSource)
.packages("com.foo.secondary.model")
.persistenceUnit("secondaryDataSource")
.properties(secondaryJpaProperties)
.build();
}
#Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(
#Qualifier("secondaryEntityManagerFactory") EntityManagerFactory secondaryEntityManagerFactory) {
return new JpaTransactionManager(secondaryEntityManagerFactory);
}
}
I also tried org.hibernate.dialect.Oracle10gDialect, and set spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect in application.properties, but nothing changed.
How can I properly configure dialect for oracle?
Spring Data JDBC does not support oracle dialect. You need to define your dialect that implements JdbcDialectProvider.
public final class OracleDialect implements DialectResolver.JdbcDialectProvider {
private static Dialect getDialect(Connection connection) throws SQLException {
DatabaseMetaData metaData = connection.getMetaData();
String name = metaData.getDatabaseProductName().toLowerCase(Locale.ROOT);
if (name.contains("oracle")) {
return AnsiDialect.INSTANCE;
}
return null;
}
#Override
public Optional<Dialect> getDialect(JdbcOperations operations) {
return Optional.ofNullable(operations.execute((ConnectionCallback<Dialect>) OracleDialect::getDialect));
}
}
Add spring-boot-starter-data-jdbc dependency in your build.gradle or pom.xml.
Then, as mentioned in the blog, create spring.factories file in resources/META-INF, and paste the following command:
org.springframework.data.jdbc.repository.config.DialectResolver$JdbcDialectProvider=<your-package>.OracleDialect
Also, since both databases you use are the same (OracleDB), you do not need to set .properties() for entity manager. As #SternK mentioned, you can have spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect in your application.properties only.

Why can't Service find my Repository bean?

I'm making a simple spring boot application and my service doesn't seem to notice my repository bean for some reason. I checked my configuration and it seems fine, I also basically just copied way that the repository is injected from a tutorial so i really don't even have an idea where might the problem be.
My Repository:
#Repository
public interface ProjectRepository extends CrudRepository<Trucks, Integer>
{
#Query("....")
List<Trucks> getTrucks();
}
My service:
#Service
#Transactional
public class ProjectServiceImpl implements ProjectService {
private ProjectRepository projectRepository;
#Autowired
public ProjectServiceImpl(ProjectRepository projectRepository) {
this.projectRepository = projectRepository;
}
#Override
public List<Trucks> getTrucks() {
return projectRepository.getTrucks();
}
}
Configuration:
#Configuration
#EnableJpaRepositories(basePackages = {
"com.javar.domain"
})
#PropertySource("classpath:application.properties")
#EnableTransactionManagement
public class PersistanceContext {
private static final String[] ENTITY_PACKAGES = {
"com.javar.domain"
};
private static final String PROPERTY_NAME_DB_DRIVER_CLASS
="spring.datasource.driver-class-name";
private String PROPERTY_NAME_DB_URL="app.datasource.url";
private String PROPERTY_NAME_DB_USER="app.datasource.username";
private String PROPERTY_NAME_DB_PASSWORD="app.datasource.password";
private String
PROPERTY_NAME_HIBERNATE_DIALECT="spring.jpa.properties.hibernate.dialect";
private String
PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO="spring.jpa.hibernate.ddl-auto";
private String
PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY="spring.jpa.hibernate.naming-
strategy";
private String PROPERTY_NAME_HIBERNATE_SHOW_SQL="spring.jpa.show-sql";
private String
PROPERTY_NAME_HIBERNATE_FORMAT_SQL=
"spring.jpa.properties.hibernate.format_sql";
#Bean(destroyMethod = "close")
DataSource dataSource(Environment env) {
HikariConfig dataSourceConfig = new HikariConfig();
dataSourceConfig.setDriverClassName(env.getRequiredProperty
(PROPERTY_NAME_DB_DRIVER_CLASS));
dataSourceConfig.setJdbcUrl(env.getRequiredPropert
(PROPERTY_NAME_DB_URL));
dataSourceConfig.setUsername(env.getRequiredProperty
(PROPERTY_NAME_DB_USER));
dataSourceConfig.setPassword(env.getRequiredProperty
(PROPERTY_NAME_DB_PASSWORD));
return new HikariDataSource(dataSourceConfig);
}
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource
dataSource, Environment env) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new
LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new
HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan(ENTITY_PACKAGES);
Properties jpaProperties = new Properties();
//Configures the used database dialect. This allows Hibernate to create SQL
//that is optimized for the used database.
jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
//Specifies the action that is invoked to the database when the Hibernate
//SessionFactory is created or closed.
jpaProperties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
//Configures the naming strategy that is used when Hibernate creates
//new database objects and schema elements
jpaProperties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
//If the value of this property is true, Hibernate writes all SQL
//statements to the console.
jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
//If the value of this property is true, Hibernate will use prettyprint
//when it writes SQL to the console.
jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
Application.properties:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
app.datasource.url=jdbc:mysql://localhost:3306/zavrsni?useSSL=false
app.datasource.username=....
app.datasource.password=....
#Hibernate Configuration
spring.jpa.properties.hibernate.dialect =
org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.hibernate.naming-strategy =
org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
And the error report:
Description:
Parameter 0 of constructor in com.javar.serviceImpl.ProjectServiceImpl
required a bean of type 'com.javar.repositoryy.ProjectRepository' that
could not be found.
Action:
Consider defining a bean of type 'com.javar.repositoryy.ProjectRepository'
in your configuration.
}
You can try this:
#Autowired
private ProjectRepository projectRepository;
and remove this contructor with that annotation:
#Autowired
public ProjectServiceImpl(ProjectRepository projectRepository) {
this.projectRepository = projectRepository;
}
If this helps you or you have some questions, let me edit this answer later.
UPDATED
Make sure that the package is correct where repository is found.
#EnableJpaRepositories(basePackages = {"com.javar.domain"})
So update the package to "com.javar.repositories"
Found the answer finally, so i just needed to add #EnableJpaRepositories(basePackages = "com.javar.repositoryy") above my Application class. Hope this helps someone in the future.
Solved this error by adding (missing in my case) maven dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Getting NoUniqueBeanDefinitionException when extends QueryDslRepositorySupport and multiple datasources

I need to use multiple databases.
I am using Spring Boot + Spring Data JPA,
so I have two configuration classes:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="com.rest.dao.first",
entityManagerFactoryRef = "firstEntityManagerFactory", transactionManagerRef = "firstTransactionManager")
public static class DnbbJdbcConfig {
#Primary
#Bean
#ConfigurationProperties(prefix="datasource.first")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "firstEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(dataSource())
.packages("com.rest.dao.first")
.persistenceUnit("first")
.build();
}
#Primary
#Bean(name = "firstTransactionManager")
PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactory(builder).getObject());
}
}
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="com.rest.dao.second",
entityManagerFactoryRef = "secondEntityManagerFactory", transactionManagerRef = "secondTransactionManager")
public static class SmsJdbcConfig {
#Bean
#ConfigurationProperties(prefix="datasource.second")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "secondEntityManagerFactory")
#PersistenceContext(unitName = "second")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(dataSource())
.packages("com.rest.dao.second")
.persistenceUnit("second")
.build();
}
#Bean(name = "secondTransactionManager")
PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactory(builder).getObject());
}
}
I guess there is not error and correct.
So, when I use default repository then not error.
(e.g userRepository.findById() - not error in multi datasources)
But, When I use custom repository then error occur.
(https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations)
Custom Implements
public class FirstRepositoryImpl extends QueryDslRepositorySupport implements FirstCustomRepository {
public FirstRepositoryImpl() {
super(First.class);
}
#PersistenceContext(unitName = "first")
private EntityManager entityManager;
private QFirst first = QFirst.first;
#Override
public List<String> messages() {
JPAQuery query = new JPAQuery(entityManager);
return query.from(first).list(first.message);
}
}
ExceptionTrace
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: firstEntityManagerFactory,secondEntityManagerFactory
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:582) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:541) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:707) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:680) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:178) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:354) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
... 45 common frames omitted
Did I misconfigure something?
I wrote sample code in my github repository..
https://github.com/okihouse/spring-boot-multiple-datasource-with-querydsl
Solved myself:
I'm check QueryDslRepositorySupport.class and found out.
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
Assert.notNull(entityManager);
this.querydsl = new Querydsl(entityManager, builder);
this.entityManager = entityManager;
}
#PersistenceContext have not "unitName"
So, Spring can't inject EntityManager.
I create QueryDslRepositorySupportWrapper.java
and inject EntityManager manually.
And It works.
https://github.com/okihouse/spring-boot-multiple-datasource-with-querydsl

Connection to two different databases in Spring JPA fails

I am trying to use 2 different data sources/databases in my application but so far I am not able to connect to them.
FirstDb Config file
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "xx.xx.xx.crud.repository.running", entityManagerFactoryRef = "dummyTestEntityManagerFactory", transactionManagerRef = "transactionManagerOne")
#PropertySource("classpath:application.properties")
public class dummyTestDbConfig {
#Value("${spring.datasourcedummyTestraid.driver-class-name}")
String driverClassName = "";
#Value("${spring.datasourcedummyTestraid.url}")
String url = "";
#Value("${spring.datasourcedummyTestraid.username}")
String userName = "";
#Value("${spring.datasourcedummyTestraid.password}")
String password = "";
#Autowired
#Qualifier("jpadummyTestVendorApapter")
JpaVendorAdapter jpadummyTestVendorApapter;
#Bean(name = "dummyTestDataSource")
#Primary
public DataSource dummyTestDataSource() {
return DataSourceBuilder.create().url(url)
.driverClassName(driverClassName).username(userName)
.password(password).build();
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean dummyTestEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dummyTestDataSource());
factoryBean.setJpaVendorAdapter(jpadummyTestVendorApapter);
factoryBean.setPackagesToScan(R.dummyTestDB_PACKAGE);
factoryBean.setPersistenceUnitName("dummyTestPersistenceUnit");
factoryBean.afterPropertiesSet();
return factoryBean;
}
#Bean
PlatformTransactionManager transactionManagerOne() {
return new JpaTransactionManager(dummyTestEntityManagerFactory()
.getObject());
}
#Bean(name = "jpadummyTestVendorApapter")
#Primary
public JpaVendorAdapter jpadummyTestVendorAdapter() {
HibernateJpaVendorAdapter jpadummyTestVendorAdapter = new HibernateJpaVendorAdapter();
jpadummyTestVendorAdapter.setShowSql(true);
jpadummyTestVendorAdapter.setGenerateDdl(true);
jpadummyTestVendorAdapter.setDatabase(Database.MYSQL);
return jpadummyTestVendorAdapter;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
Second db config
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "xx.xx.xx.xx.xx.running", entityManagerFactoryRef = "alpharaidEntityManagerFactory", transactionManagerRef = "transactionManagertwo")
#PropertySource("classpath:application.properties")
public class alphaDbConfig {
#Value("${spring.datasourcealpharaid.driver-class-name}")
String driverClassName = "";
#Value("${spring.datasourcealpharaid.url}")
String url = "";
#Value("${spring.datasourcealpharaid.username}")
String userName = "";
#Value("${spring.datasourcealpharaid.password}")
String password = "";
#Autowired
#Qualifier("jpaMyalphaVendorAdapter")
JpaVendorAdapter myalphaVendorAdapter;
#Bean(name = "alpharaidDataSource")
public DataSource alpharaidDataSource() {
return DataSourceBuilder.create().url(url)
.driverClassName(driverClassName).username(userName)
.password(password).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean alpharaidEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(alpharaidDataSource());
factoryBean.setJpaVendorAdapter(myalphaVendorAdapter);
factoryBean.setPackagesToScan(R.alphaDB_PACKAGE);
factoryBean.setPersistenceUnitName("alpharaidPersistenceUnit");
factoryBean.afterPropertiesSet();
return factoryBean;
}
#Bean
PlatformTransactionManager transactionManagerTwo() {
return new JpaTransactionManager(alpharaidEntityManagerFactory()
.getObject());
}
#Bean(name = "jpaMyalphaVendorAdapter")
public JpaVendorAdapter jpaMyalphaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setDatabase(Database.MYSQL);
return jpaVendorAdapter;
}
}
when i start server, i recieve Exception
2015-08-19 15:21:25.412 ERROR 16928 --- [ main] org.hibernate.tool.hbm2ddl.SchemaUpdate : HHH000299: Could not complete schema update
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Access denied for user 'xyz'#'192.168.XX.XX' to database 'xyz'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:377)
at com.mysql.jdbc.Util.getInstance(Util.java:360)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:978)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3887)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3823)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:870)
at com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:1659)
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1206)
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2234)
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2265)
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2064)
at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:790)
at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:44)
and Another one in same Exception (though little later)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: alpharaidEntityManagerFactory,dummyTestEntityManagerFactory
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:572)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:531)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:697)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:670)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:354)
... 29 more
My properties files to read connection Details
spring.datasourcedummyTestraid.driver-class-name=com.mysql.jdbc.Driver
spring.datasourcedummyTestraid.url=jdbc:mysql://xx.xx.xx.xx/xyz
spring.datasourcedummyTestraid.username=xxx
spring.datasourcedummyTestraid.password=xxx
spring.datasourcealpharaid.driver-class-name=com.mysql.jdbc.Driver
spring.datasourcealpharaid.url=jdbc:mysql://xx.xx.xx.xx/abc
spring.datasourcealpharaid.username=xxx
spring.datasourcealpharaid.password=xxx
Few Observations:
In exception it can be seen it is trying to connect to my local db ,192.168.XX.XX is my local address.
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Access denied for user 'xyz'#'192.168.XX.XX' to database 'xyz'
(which do not exist) ,I am trying to connect to 2 different remote db's as defined in Properties file.why it goes to local?
when i comment one of the config then other runs fine (though no access problem comes)
problem of
No qualifying bean of type [javax.persistence.EntityManagerFactory] for this i have tried to make one of the Bean primary,tried making it simple function but of no use.
Please help me in solving the problem . Thanks.
You can avoid doing this
#Value("${spring.datasourcealpharaid.driver-class-name}")
String driverClassName = "";
#Value("${spring.datasourcealpharaid.url}")
String url = "";
#Value("${spring.datasourcealpharaid.username}")
String userName = "";
#Value("${spring.datasourcealpharaid.password}")
String password = "";
#Autowired
#Qualifier("jpaMyAlphaVendorAdapter")
JpaVendorAdapter myAlphaVendorAdapter;
#Bean(name = "dummyTestDataSource")
public DataSource dummyTestDataSource() {
return DataSourceBuilder.create().url(url)
.driverClassName(driverClassName).username(userName)
.password(password).build();
}
and instead reduce your code with the lines below:
#ConfigurationProperties(prefix = "spring.datasourcealpharaid")
#Bean
#Primary
public DataSource postgresDataSource() {
return DataSourceBuilder.create().
build();
}
In order to avoid the exception below you can name your bean as a entityManagerFactory
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: macraidEntityManagerFactory,nextGenEntityManagerFactory
#Bean(name = "entityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean emf1(EntityManagerFactoryBuilder builder){
return builder
.dataSource(postgresDataSource())
.packages("io.eddumelendez.springdatajpa.domain1")
.persistenceUnit("users")
.build();
}
Checkout the complete configuration here

Resources