I want to configure 2 JNDI datasources in Spring Boot. I tried this configuration:
application.properties
spring.production-datasource.jndi-name=java:/global/production_gateway
spring.production-datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
spring.warehouse-datasource.jndi-name=java:/global/production_warehouse
spring.warehouse-datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
Primary datasource configuration:
@Configuration
@EnableJpaRepositories(
basePackages = "org.datalis.plugin.production.entity",
entityManagerFactoryRef = "productionEntityManager",
transactionManagerRef = "productionTransactionManager"
)
@EnableTransactionManagement
public class ContextProductionDatasource {
@Autowired
private Environment env;
@Primary
@Bean(name = "productionDataSourceProperties")
@ConfigurationProperties(prefix="spring.production.datasource")
public DataSourceProperties productionDataSourceProperties() {
return new DataSourceProperties();
}
@Primary
@Bean(name = "productionDataSource")
@ConfigurationProperties(prefix="spring.production.datasource")
public DataSource productionDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "productionEntityManager")
public EntityManager productionEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
@Primary
@Bean(name = "productionTransactionManager")
public PlatformTransactionManager productionTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Primary
@Bean(name = "productionExceptionTranslation")
public PersistenceExceptionTranslationPostProcessor productionExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
Second datasource configuration:
@Configuration
@EnableJpaRepositories(
basePackages = "org.datalis.plugin.warehouse.entity",
entityManagerFactoryRef = "warehouseEntityManager",
transactionManagerRef = "warehouseTransactionManager"
)
@EnableTransactionManagement
public class ContextWarehouseDatasource {
@Autowired
private Environment env;
@Bean(name = "warehouseDataSourceProperties")
@ConfigurationProperties(prefix="spring.warehouse.datasource")
public DataSourceProperties warehouseDataSourceProperties() {
return new DataSourceProperties();
}
@Bean(name = "warehouseDataSource")
@ConfigurationProperties(prefix="spring.warehouse.datasource")
public DataSource warehouseDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "warehouseEntityManager")
public EntityManager warehouseEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
@Bean(name = "warehouseTransactionManager")
public PlatformTransactionManager warehouseTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Bean(name = "warehouseExceptionTranslation")
public PersistenceExceptionTranslationPostProcessor warehouseExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
But I get error during deployment:
Caused by: java.lang.IllegalStateException: Unable to set value for property driver-class-name
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.JavaBeanBinder$BeanProperty.setValue(JavaBeanBinder.java:349)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:96)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:79)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:56)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$5(Binder.java:452)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:570)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:556)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.Binder$Context.access$400(Binder.java:513)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:450)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:391)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:320)
... 132 more
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at deployment.datalis_rest_api.war//org.springframework.boot.context.properties.bind.JavaBeanBinder$BeanProperty.setValue(JavaBeanBinder.java:346)
... 142 more
Caused by: java.lang.RuntimeException: Failed to load driver class org.mariadb.jdbc.Driver in either of HikariConfig class loader or Thread context classloader
at deployment.datalis_rest_api.war//com.zaxxer.hikari.HikariConfig.setDriverClassName(HikariConfig.java:485)
I'm using Spring boot war deployed into Wildfly server with deployed mariadb-java-client-2.4.2.jar. It's working fine when I use single datasource configuration. But with second datasource I get exception.
Do you know how I can fix this issue?
Second attempt:
application.properties:
spring.production.datasource.jndi-name=java:/global/production_gateway
spring.production.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.production.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.production.jpa.show-sql = true
spring.production.jpa.hibernate.ddl-auto = update
spring.warehouse.datasource.jndi-name=java:/global/production_warehouse
spring.warehouse.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.warehouse.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.warehouse.jpa.show-sql = true
spring.warehouse.jpa.hibernate.ddl-auto = update
First datasource:
@Configuration
@EnableJpaRepositories(
basePackages = "org.datalis.plugin.production.entity",
entityManagerFactoryRef = "productionEntityManager",
transactionManagerRef = "productionTransactionManager"
)
@EnableTransactionManagement
public class ContextProductionDatasource {
@Primary
@Bean(name = "productionDataSourceProperties")
@ConfigurationProperties(prefix="spring.production")
public DataSourceProperties productionDataSourceProperties() {
return new DataSourceProperties();
}
@Primary
@Bean(name = "productionDataSource")
@ConfigurationProperties(prefix="spring.production")
public DataSource productionDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "productionEntityManager")
public EntityManager productionEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
@Primary
@Bean(name = "productionTransactionManager")
public PlatformTransactionManager productionTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Primary
@Bean(name = "productionExceptionTranslation")
public PersistenceExceptionTranslationPostProcessor productionExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
Second datasource:
@Configuration
@EnableJpaRepositories(
basePackages = "org.datalis.plugin.warehouse.entity",
entityManagerFactoryRef = "warehouseEntityManager",
transactionManagerRef = "warehouseTransactionManager"
)
@EnableTransactionManagement
public class ContextWarehouseDatasource {
@Bean(name = "warehouseDataSourceProperties")
@ConfigurationProperties(prefix="spring.warehouse")
public DataSourceProperties warehouseDataSourceProperties() {
return new DataSourceProperties();
}
@Bean(name = "warehouseDataSource")
@ConfigurationProperties(prefix="spring.warehouse")
public DataSource warehouseDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "warehouseEntityManager")
public EntityManager warehouseEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
@Bean(name = "warehouseTransactionManager")
public PlatformTransactionManager warehouseTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Bean(name = "warehouseExceptionTranslation")
public PersistenceExceptionTranslationPostProcessor warehouseExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
Now I get:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'entityManagerFactory' threw exception; nested exception is java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
at deployment.datalis_rest_api.war//org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at deployment.datalis_rest_api.war//org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:640)
... 41 more
Caused by: java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
correct the name of both entityManager beans: productionEntityManager and warehouseEntityManager
entityManagerFactoryRef
in @EnableJpaRepositories
is the beanName reference,
so use
@Configuration
@EnableJpaRepositories(
basePackages = "org.datalis.plugin.production.entity",
entityManagerFactoryRef = "productionEntityManager",
transactionManagerRef = "productionTransactionManager"
)
@EnableTransactionManagement
public class ContextProductionDatasource {
...
@Primary
@Bean
public EntityManager productionEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
and
@Configuration
@EnableJpaRepositories(
basePackages = "org.datalis.plugin.warehouse.entity",
entityManagerFactoryRef = "warehouseEntityManager",
transactionManagerRef = "warehouseTransactionManager"
)
@EnableTransactionManagement
public class ContextWarehouseDatasource {
...
@Bean
public EntityManager warehouseEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
you need to match the property-name in @ConfigurationProperties
to the value of properties '-' versus '.'
use @ConfigurationProperties(prefix="spring.production-datasource")
and
@ConfigurationProperties(prefix="spring.warehouse-datasource")
and make sure you have have driver in your classpath
like
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.5.2</version>
<scope>runtime</scope>
</dependency>
In your properties you are using spring.production-datasource
however in the Spring @Configuration
you are attempting to map it with @ConfigurationProperties(prefix = "spring.production.datasource")
. Starting from Spring Boot 2 the relaxed property binding rules are now more strict so there is a mismatch between a dash and a dot here.
You either need to change properties to spring.production.datasource
or use @ConfigurationProperties(prefix = "spring.production-datasource")
.
When configuring Spring Boot DataSource
you can either use jndi-name
or provide the connection details, not both. As per Common Application properties:
spring.datasource.jndi-name
JNDI location of the datasource. Class, url, username & password are ignored
You most likely have to remove spring.production-datasource.driver-class-name
property as it collides with jndi-name
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With