9

I have springBatch App where I was using multiple datasources which I configured as below which is pretty much straight forward. It was working fine.

How to use 2 or more databases with spring?

Now I had to integrate it to Jhipster project. I did similar kind of configuration in application.yml. I removed auto generated Datasource from application.yml and added configurations similar to above post and injected the primary Datasource in DataBaseConfiguration.java which is Jhipster generated class. With this configuration I am unable to do CRUD operation on Database entities form JHipster UI. I am not seeing any errors in logs.

I am not sure what's the right/simple way of configuring multiple datasources in Jhipster project. A sample example utilizing multiple databases would give a good start for me. I didn't find much resources on this.

Below are code sample's for changes I performed to have multiple datasources in JHipster

# ===================================================================
# Spring Boot configuration for the "dev" profile.
#
# This configuration overrides the application.yml file.
#
# More information on profiles: http://www.jhipster.tech/profiles/
# More information on configuration properties: http://www.jhipster.tech/common-application-properties/
# ===================================================================

# ===================================================================
# Standard Spring Boot properties.
# Full reference is available at:
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================

spring:
    profiles:
        active: dev
        include: swagger
    devtools:
        restart:
            enabled: true
        livereload:
            enabled: false # we use gulp + BrowserSync for livereload
    jackson:
        serialization.indent_output: true

    jpa:
        database-platform: org.hibernate.dialect.Oracle12cDialect
        database: ORACLE
        show-sql: true
        properties:
            hibernate.id.new_generator_mappings: true
            hibernate.cache.use_second_level_cache: false
            hibernate.cache.use_query_cache: false
            hibernate.generate_statistics: true
            hibernate.default_schema: ********x
    mail:
        host: localhost
        port: 25
        username:
        password:
    messages:
        cache-seconds: 1
    thymeleaf:
        cache: false

    batch:
        job:
          enabled: false

liquibase:
    contexts: dev

# ===================================================================
# To enable SSL, generate a certificate using:
# keytool -genkey -alias ********x-storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
#
# You can also use Let's Encrypt:
# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
#
# Then, modify the server.ssl properties so your "server" configuration looks like:
#
# server:
#    port: 8443
#    ssl:
#        key-store: keystore.p12
#        key-store-password: <your-password>
#        keyStoreType: PKCS12
#        keyAlias: ********x
# ===================================================================
server:
    port: 8080

# ===================================================================
# JHipster specific properties
#
# Full reference is available at: http://www.jhipster.tech/common-application-properties/
# ===================================================================

jhipster:
    http:
        version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration)
    # CORS is only enabled by default with the "dev" profile, so BrowserSync can access the API
    cors:
        allowed-origins: "*"
        allowed-methods: "*"
        allowed-headers: "*"
        exposed-headers: "Authorization"
        allow-credentials: true
        max-age: 1800
    security:
        authentication:
            jwt:
                secret: my-secret-token-to-change-in-production
                # Token is valid 24 hours
                token-validity-in-seconds: 86400
                token-validity-in-seconds-for-remember-me: 2592000
    mail: # specific JHipster mail property, for standard properties see MailProperties
        from: ********x@localhost
        base-url: http://127.0.0.1:8080
    metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
        jmx.enabled: true
        graphite: # Use the "graphite" Maven profile to have the Graphite dependencies
            enabled: false
            host: localhost
            port: 2003
            prefix: ********x
        prometheus: # Use the "prometheus" Maven profile to have the Prometheus dependencies
            enabled: false
            endpoint: /prometheusMetrics
        logs: # Reports Dropwizard metrics in the logs
            enabled: false
            report-frequency: 60 # in seconds
    logging:
        logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
            enabled: false
            host: localhost
            port: 5000
            queue-size: 512

# ===================================================================
# Application specific properties
# Add your own application properties here, see the ApplicationProperties class
# to have type-safe configuration, like in the JHipsterProperties above
#
# More documentation is available at:
# http://www.jhipster.tech/common-application-properties/
# ===================================================================



datasource.********x.type: com.zaxxer.hikari.HikariDataSource
datasource.********x.url: "jdbc:oracle:thin:@********x"
datasource.********x.username: ********x
datasource.********x.password: ********x

datasource.********xy.type: com.zaxxer.hikari.HikariDataSource
datasource.********xy.url: "jdbc:oracle:thin:@yyyyyy"
datasource.********xy.username:********x
datasource.********xy.password: "********x"




package com.********x

import io.github.jhipster.config.JHipsterConstants;
import io.github.jhipster.config.liquibase.AsyncSpringLiquibase;

import liquibase.integration.spring.SpringLiquibase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@EnableJpaRepositories("com.********x")
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
@EnableTransactionManagement
public class DatabaseConfiguration {

    private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);

    private final Environment env;

    public DatabaseConfiguration(Environment env) {
        this.env = env;
    }

    @Autowired
    @Qualifier("********x")
    private DataSource dataSource;

    @Bean
    public SpringLiquibase liquibase(@Qualifier("taskExecutor") TaskExecutor taskExecutor
            , LiquibaseProperties liquibaseProperties) {

        // Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously
        SpringLiquibase liquibase = new AsyncSpringLiquibase(taskExecutor, env);
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:config/liquibase/master.xml");
        liquibase.setContexts(liquibaseProperties.getContexts());
        liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
        liquibase.setDropFirst(liquibaseProperties.isDropFirst());
        if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) {
            liquibase.setShouldRun(false);
        } else {
            liquibase.setShouldRun(liquibaseProperties.isEnabled());
            log.debug("Configuring Liquibase");
        }
        return liquibase;
    }
}

So I injected primary Datasource in DatabaseConfiguration.java and using other datasource where required. Both these datasources are created similar to the above post like how spring suggests.

MetaColon
  • 2,895
  • 3
  • 16
  • 38
karthik
  • 773
  • 2
  • 11
  • 19

2 Answers2

11

Here is how I make two datasource work in JHipster project :

The default datasource properties generate by JHipster:

spring:
    datasource:
        type: com.zaxxer.hikari.HikariDataSource
        url: jdbc:h2:file:./target/h2db/db/mockpartenaire;DB_CLOSE_DELAY=-1
        username: mockpartenaire
        password:

Configs for the JHispter default datasource :

@Configuration
@EnableTransactionManagement
public class PrimaryDbConfig {

@Bean
@Primary
@ConfigurationProperties("spring.datasource")
public DataSourceProperties defaultDataSourceProperties() {
    return new DataSourceProperties();
}

@Bean
@Primary
@ConfigurationProperties("spring.datasource")
public DataSource defaultDataSource() {
    return defaultDataSourceProperties().initializeDataSourceBuilder().build();
}

@Bean(name = "entityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
    EntityManagerFactoryBuilder builder) {
    return builder
        .dataSource(defaultDataSource())
        .packages(Input.class)
        .persistenceUnit("default")
        .build();
}

@Bean(name = "transactionManager")
@Primary
public JpaTransactionManager db2TransactionManager(@Qualifier("entityManagerFactory") final EntityManagerFactory emf) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(emf);
    return transactionManager;
}

}

The second datasource properties:

partner:
    datasource:
        type: com.zaxxer.hikari.HikariDataSource
        url: jdbc:postgresql://localhost:5432/partner
        username: partner
        password: partner
        driver-class-name: org.postgresql.Driver

The second datasource configs :

@Configuration
@EnableTransactionManagement
@EntityScan(basePackages = "com.my.test.custom.domain")
@EnableJpaRepositories(transactionManagerRef = "partnerTransactionManager", entityManagerFactoryRef = "partnerEntityManagerFactory", basePackages = "com.my.test.custom.repository")
public class PartnerDbConfig {

    @Bean
    @ConfigurationProperties("partner.datasource")
    public DataSourceProperties partnerDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("partner.datasource")
    public DataSource partnerDataSource() {
        return partnerDataSourceProperties().initializeDataSourceBuilder().build();
    }

    @Bean(name = "partnerEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
        EntityManagerFactoryBuilder builder) {
        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");

        LocalContainerEntityManagerFactoryBean emf = builder
            .dataSource(partnerDataSource())
            .packages(TestPost.class)
            .persistenceUnit("partner")
            .build();
        emf.setJpaProperties(properties);
        return emf;
    }

    @Bean(name = "partnerTransactionManager")
    public JpaTransactionManager db2TransactionManager(@Qualifier("partnerEntityManagerFactory") final EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }
}

The default datasource configured by JHipster will continue to work it's entities. For your custom entities using the second datasource, here is the repository configuration :

@SuppressWarnings("unused")
@Repository
@Transactional("partnerTransactionManager")
@PersistenceContext(name = "partnerEntityManagerFactory")
public interface TestPostRepository extends  JpaRepository<TestPost,Long>{
}
freemanpolys
  • 1,848
  • 20
  • 19
  • 3
    Could you explain the reason behind creating JpaTransactionManager,LocalContainerEntityManagerFactoryBean and how you came up with this solution.Thank you. @freemanpolys – karthik Jan 02 '18 at 19:32
  • 1
    I am unable to find the locations of the configuration file. Please mention the location/path. – Pravinraj Venkatachalam Dec 03 '18 at 09:05
  • in class DatabaseConfiguration.java – Derek Feb 11 '19 at 09:38
  • @freemanpolis below is the error I am getting while running an application `*************************** APPLICATION FAILED TO START *************************** Description: Failed to bind properties under 'spring.datasource.type' to java.lang.Class: Property: spring.datasource.type Value: com.zaxxer.hikari.HikariDataSource Origin: class path resource [config/application-dev.yml]:27:15 Reason: No converter found capable of converting from type [java.lang.String] to type [java.lang.Class]` – Sadashiv Jun 25 '19 at 08:50
  • Note that I had to add `auto-commit: false` to the partner datasource (for Oracle), in case anyone else is running into problems there too – MetaColon Jun 09 '20 at 10:34
  • @MetaColon where did you add the auto-commit tag – Papa Daouda NIANG Mar 09 '22 at 13:15
  • @PapaDaoudaNIANG I have no clue anymore - this was two years ago, which also was about the last time I worked with JHipster. Maybe the other answer can point you into the right direction though. – MetaColon Mar 10 '22 at 14:42
0

I found @freemanpolys answer great but it needs an extra property if you want it to work with postGres.

Add the following property to both the default and secondary datasource:

autocommit: false
Grant Lay
  • 800
  • 7
  • 17