Manu Manu - 4 months ago 304
Java Question

Configure Mutiple DataSource in Spring Boot with JNDI

I want to manage mutiple DataSource using your Application Servers built-in features and access it using JNDI. I am using Spring boot with Spring JPA data.

I am able to configure the application.properties for single datasource:-

spring.datasource.jndi-name=jdbc/customers


And my configuration in context.xml file as below:-

<Resource name="jdbc/customer" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/customer"/>


Everything works fine.

But when I am unable to configure for two datasource.

I am sure on the configuration in context.xml file:-

<Resource name="jdbc/customer" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/customer"/>

<Resource name="jdbc/employee" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/employee"/>


I am in doubt about the application.properties file configuration.

I tried the below options with no success:-

spring.datasource.jndi-name=jdbc/customers,jdbc/employee


Please let me know any details on Spring boot with JNDI for mutiple data source. I was looking for this configuration for days now. Any kind of suggestion or comments is hight appreciated.

Second Trial As per Spring Boot Documentation

spring.datasource.primary.jndi-name=jdbc/customer
spring.datasource.secondary.jndi-name=jdbc/project


Configuration class.

@Bean
@Primary
@ConfigurationProperties(prefix="datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}

@Bean
@ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}


The application does not get started. Though the tomcat server is getting started. No errors are printed in the log.

Third Trial :- With JndiObjectFactoryBean

I have the below application.properties

spring.datasource.primary.expected-type=javax.sql.DataSource
spring.datasource.primary.jndi-name=jdbc/customer
spring.datasource.primary.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.datasource.primary.jpa.show-sql=false
spring.datasource.primary.jpa.hibernate.ddl-auto=validate

spring.datasource.secondary.jndi-name=jdbc/employee
spring.datasource.secondary.expected-type=javax.sql.DataSource
spring.datasource.secondary.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.datasource.secondary.jpa.show-sql=false
spring.datasource.secondary.jpa.hibernate.ddl-auto=validate


And the below java configuration:-

@Bean(destroyMethod="")
@Primary
@ConfigurationProperties(prefix="spring.datasource.primary")
public FactoryBean primaryDataSource() {
return new JndiObjectFactoryBean();
}

@Bean(destroyMethod="")
@ConfigurationProperties(prefix="spring.datasource.secondary")
public FactoryBean secondaryDataSource() {
return new JndiObjectFactoryBean();
}


But still getting error:-

Related cause: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'primaryDataSource' defined in class path resource [com/web/initializer/MvcConfig.class]: Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name [jdbc/customer] is not bound in this Context. Unable to find [jdbc].
Related cause: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'secondaryDataSource' defined in class path resource [com/web/initializer/MvcConfig.class]: Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name [jdbc/employee] is not bound in this Context. Unable to find [jdbc].
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
at org.springframework.boot.context.web.SpringBootServletInitializer.run(SpringBootServletInitializer.java:117)
at org.springframework.boot.context.web.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:108)
at org.springframework.boot.context.web.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:68)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:175)


Update:-
Trial using the below properties file:-

spring.datasource.primary.expected-type=javax.sql.DataSource
spring.datasource.primary.jndi-name=java:comp/env/jdbc/customer

spring.datasource.secondary.jndi-name=java:comp/env/jdbc/employee
spring.datasource.secondary.expected-type=javax.sql.DataSource

spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=validate


It creates all the tables in customer schema , but fails trying to find the other tables also.(from the second schema)

Answer

This is the solution for your third trial a little bit modified. Consider this solution (Spring Boot 1.3.2):

application.properties file:

spring.datasource.primary.jndi-name=java:/comp/env/jdbc/SecurityDS
spring.datasource.primary.driver-class-name=org.postgresql.Driver

spring.datasource.secondary.jndi-name=java:/comp/env/jdbc/TmsDS
spring.datasource.secondary.driver-class-name=org.postgresql.Driver

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.show-sql=false

Configuration:

@Configuration@ EnableConfigurationProperties
public class AppConfig {

    @Bean@ ConfigurationProperties(prefix = "spring.datasource.primary")
    public JndiPropertyHolder primary() {
        return new JndiPropertyHolder();
    }

    @Bean@ Primary
    public DataSource primaryDataSource() {
        JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
        DataSource dataSource = dataSourceLookup.getDataSource(primary().getJndiName());
        return dataSource;
    }

    @Bean@ ConfigurationProperties(prefix = "spring.datasource.secondary")
    public JndiPropertyHolder secondary() {
        return new JndiPropertyHolder();
    }

    @Bean
    public DataSource secondaryDataSource() {
        JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
        DataSource dataSource = dataSourceLookup.getDataSource(secondary().getJndiName());
        return dataSource;
    }

    private static class JndiPropertyHolder {
        private String jndiName;

        public String getJndiName() {
            return jndiName;
        }

        public void setJndiName(String jndiName) {
            this.jndiName = jndiName;
        }
    }
}

And then you can follow guide http://docs.spring.io/spring-data/jpa/docs/1.3.0.RELEASE/reference/html/jpa.repositories.html to use your datasources with jpa repositories.