Configure Multiple Databases Spring JPA Spring Boot

Overview

In this article, we will explore a simple Spring Boot application to implement a simple Spring configuration for a Spring Data JPA with multiple databases requirements.

Create an application to store information related to Books in a database named book-database.

In the same application, we want to store information related to Author in a separate database named author-database.

Now, create the entities for both domains:

Entities classes

First, let’s create a simple entity class for the Book domain.

Here is the first Book entity:

package com.dineshonjava.myapp.model.book;

@Entity
@Table(schema = "books")
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int bookId;

    private String title;

    private String author;

    private String publisher;

    private double price;
}

Now, let’s create a simple entity class for the Author domain.

package com.dineshonjava.myapp.model.author;

@Entity
@Table(schema = "authors")
public class Author {

    @Id
    private int authorId;

    private String name;

    private String email;
}

As you can see both entity classes, we have placed them in independent packages.

Let’s create JPA repositories for both entity classes.

The JPA Repositories

Let’s create a JPA repository class for the Book entity, BookRepository:

package com.dineshonjava.myapp.dao.book;

public interface BookRepository
  extends JpaRepository<Book, Integer> { }

Next, let’s create another repository class for Author, AuthorRepository:

package com.dineshonjava.myapp.dao.author;

public interface AuthorRepository
  extends JpaRepository<Author, Integer> { }

Again we have placed both repositories into different packages due to separate databases.

Let’s move to create configurations for both databases using Spring Boot. We can simply add this DB configuration into an application.properties file.

By default, Spring Boot will instantiate its default DataSource with the configuration properties prefixed by spring.datasource.* but we can create separate prefixed values as well.

Database configurations

Let’s add the following database configuration for the book database into the application.properties file.

spring.datasource.jdbcUrl = <<book-database-url>>
spring.datasource.username = <<book-database-username>>
spring.datasource.password = <<book-database-passwaord>>

Let’s the follow configuration related to the second database using the same way to configure the second DataSource as below:

spring.author-datasource.jdbcUrl = <<author-database-url>>
spring.author-datasource.username = <<author-database-username>>
spring.author-datasource.password = <<author-database-passwaord>>

Now, we need to create different Spring Boot autoconfiguration files to pick up both database configurations from the properties file as added above for two different DataSources.

Spring Boot autoconfiguration

Let’s create Spring Boot autoconfiguration for the book database.

@Configuration
@EnableJpaRepositories(
  basePackages = "com.dineshonjava.myapp.dao.book",
  entityManagerFactoryRef = "bookEntityManager",
  transactionManagerRef = "bookTransactionManager")
public class PersistenceBookAutoConfiguration {
    
    @Primary
    @Bean
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource bookDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean bookEntityManager() {
        LocalContainerEntityManagerFactoryBean em
                = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(bookDataSource());
        em.setPackagesToScan(
                new String[] { "com.dineshonjava.myapp.model.books"});

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
                env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
                env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }
    @Bean
    @Primary
    public PlatformTransactionManager bookTransactionManager() {

        JpaTransactionManager transactionManager
                = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
                entityManager().getObject());
        return transactionManager;
    }
}

Next, let’s create Spring Boot autoconfiguration for the author database.

@Configuration
@EnableJpaRepositories(
  basePackages = "com.dineshonjava.myapp.dao.author", 
  entityManagerFactoryRef = "authorEntityManager", 
  transactionManagerRef = "authorTransactionManager")
public class PersistenceAuthorAutoConfiguration {
   
    @Bean
    @ConfigurationProperties(prefix="spring.author-datasource")
    public DataSource authorDataSource() {
        return DataSourceBuilder.create().build();
    }
   
    @Bean
    public LocalContainerEntityManagerFactoryBean authorEntityManager() {
        LocalContainerEntityManagerFactoryBean em
                = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(bookDataSource());
        em.setPackagesToScan(
                new String[] { "com.dineshonjava.myapp.model.authors"});

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
                env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
                env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    public PlatformTransactionManager authorTransactionManager() {

        JpaTransactionManager transactionManager
                = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
                entityManager().getObject());
        return transactionManager;
    }
}

Now we have defined the data source properties inside the application.properties according to the Boot autoconfiguration convention.

You can see the above configuration files. These are annotating the data source bean creation method with @ConfigurationProperties and we have used the corresponding config prefix.

DataSourceBuilder of Spring Boot takes care automatically how to create DataSource for each database using defined configurations.

Summary

We have looked at a practical overview of how we can configure multiple databases into a Spring Boot application.

Previous
Next