How to add multiple database support to Spring application

Define multiple configuration files. In each configuration file, specify the JpaVendorAdapter, datasource, enityManager, entityManagerFactory, and transactionManager. You can set a default configuration file and multiple other configuration files.

A sample goes as below:

Default configuration file:

@Configuration
@EnableTransactionManagement
@EnableAutoConfiguration
@EnableJpaRepositories(
        entityManagerFactoryRef = "entityManagerFactory",
        transactionManagerRef = "transactionManager",
        basePackages = {"bmo.hub.bpm.foundation.appmonitor.db.repo"}
)
public class AppConfig {

    @Autowired
    private JpaVendorAdapter jpaVendorAdapter;

    @Autowired
    private DataSource dataSource;

    @Bean(name = "entityManager")
    public EntityManager entityManager() {
        return entityManagerFactory().createEntityManager();
    }

    @Primary
    @Bean(name = "entityManagerFactory")
    public EntityManagerFactory entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource);
        emf.setJpaVendorAdapter(jpaVendorAdapter);
        emf.setPackagesToScan("bmo.hub.bpm.foundation.appmonitor.db.entity");
        emf.setPersistenceUnitName("default");
        emf.afterPropertiesSet();

        return emf.getObject();
    }

    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(entityManagerFactory());

        return tm;
    }
}

Another configuration file:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "mortgageEntityManagerFactory",
        transactionManagerRef = "mortgageTransactionManager",
        basePackages = {"bmo.hub.bpm.foundation.appmonitor.db.repo.mortgage"}
)
public class MortgageConfig {

    @Autowired
    private JpaVendorAdapter jpaVendorAdapter;

//    @Autowired
//    private DataSource dataSource;
    @Value("${spring.datasource.mortgage.url}")
    private String databaseUrl;

    @Value("${spring.datasource.mortgage.username}")
    private String username;

    @Value("${spring.datasource.mortgage.password}")
    private String password;

    @Value("${spring.datasource.mortgage.driver-class}")
    private String driverClass;

    @Bean(name = "mortgageEntityManager")
    public EntityManager entityManager() {
        return entityManagerFactory().createEntityManager();
    }

    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource(databaseUrl, username, password);
        dataSource.setDriverClassName(driverClass);
        return dataSource;
    }

    @Primary
    @Bean(name = "mortgageEntityManagerFactory")
    public EntityManagerFactory entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource());
        emf.setJpaVendorAdapter(jpaVendorAdapter);
        emf.setPackagesToScan("bmo.hub.bpm.foundation.appmonitor.db.entity.mortgage");
        emf.afterPropertiesSet();

        return emf.getObject();
    }

    @Bean(name = "mortgageTransactionManager")
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(entityManagerFactory());

        return tm;
    }
}

And the properties file (application.yml):

selenium:
  grid:
    url: http://localhost:4444/wd/hub

scheduling:
  job:
    cron: 0 0/3 * * * *

spring:
  datasource:
    url: jdbc:oracle:thin:@10.193.61.167:1521:garnish
    username: row_dev
    password: Welcome#1
    driver-class: oracle.jdbc.driver.OracleDriver

    mortgage:
      url: jdbc:oracle:thin:@10.193.46.193:1521:mortgage
      username: mortgage
      password: MORTGAGE
      driver-class: oracle.jdbc.driver.OracleDriver

    hikari:
      connection-timeout: 60000
      maximum-pool-size: 5

 

Create scheduled task in Spring Boot

Reference: https://spring.io/guides/gs/scheduling-tasks/

pom.xml:

<project xmlns=”http://maven.apache.org/POM/4.0.0&#8243; xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221;
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd”&gt;
<modelVersion>4.0.0</modelVersion>

<groupId>com.sample</groupId>
<artifactId>gs-scheduling-tasks</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

ScheduledTasks.java

package com.sample;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.Date;

/**
* Created by vagrant on 5/25/17.
*/
@Component
public class ScheduledTasks {

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

private static final SimpleDateFormat dateFormat = new SimpleDateFormat(“HH:mm:ss”);

@Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
log.info(“The time is now {}”, dateFormat.format(new Date()));
}
}

App.java

package com.sample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
* Hello world!
*
*/
@SpringBootApplication
@EnableScheduling
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class);
}
}

To run: mvn spring-boot:run

A wonderful post on wrapping Spring SecurityContextHolder

From: http://stackoverflow.com/questions/360520/unit-testing-with-spring-security

You are quite right to be concerned – static method calls are particularly problematic for unit testing as you cannot easily mock your dependencies. What I am going to show you is how to let the Spring IoC container do the dirty work for you, leaving you with neat, testable code. SecurityContextHolder is a framework class and while it may be ok for your low-level security code to be tied to it, you probably want to expose a neater interface to your UI components (i.e. controllers).

cliff.meyers mentioned one way around it – create your own “principal” type and inject an instance into consumers. The Spring <aop:scoped-proxy/> tag introduced in 2.x combined with a request scope bean definition, and the factory-method support may be the ticket to the most readable code.

It could work like following:

public class MyUserDetails implements UserDetails {
    // this is your custom UserDetails implementation to serve as a principal
    // implement the Spring methods and add your own methods as appropriate
}

public class MyUserHolder {
    public static MyUserDetails getUserDetails() {
        Authentication a = SecurityContextHolder.getContext().getAuthentication();
        if (a == null) {
            return null;
        } else {
            return (MyUserDetails) a.getPrincipal();
        }
    }
}

public class MyUserAwareController {        
    MyUserDetails currentUser;

    public void setCurrentUser(MyUserDetails currentUser) { 
        this.currentUser = currentUser;
    }

    // controller code
}

Nothing complicated so far, right? In fact you probably had to do most of this already. Next, in your bean context define a request-scoped bean to hold the principal:

<bean id="userDetails" class="MyUserHolder" factory-method="getUserDetails" scope="request">
    <aop:scoped-proxy/>
</bean>

<bean id="controller" class="MyUserAwareController">
    <property name="currentUser" ref="userDetails"/>
    <!-- other props -->
</bean>

Thanks to the magic of the aop:scoped-proxy tag, the static method getUserDetails will be called every time a new HTTP request comes in and any references to the currentUser property will be resolved correctly. Now unit testing becomes trivial:

protected void setUp() {
    // existing init code

    MyUserDetails user = new MyUserDetails();
    // set up user as you wish
    controller.setCurrentUser(user);
}

Hope this helps!