EzyJPA: Connect Multi Datasources

Nowaday, we usually work with microservice system, so, we will have a requirement to fetch data from many datasources. With EzyJPA this task is so simple.

1. Configuration

Let's say we need connect to 2 datasource named author and book for book management system. For first step, we need add to the configuration file like this:
# for application.yaml
author:
  datasource:
    jdbcUrl: jdbc:mysql://root:12345678@localhost:3306/author
    driverClassName: com.mysql.cj.jdbc.Driver
book:
  datasource:
    jdbcUrl: jdbc:mysql://root:12345678@localhost:3306/book
    driverClassName: com.mysql.cj.jdbc.Driver
# for application.properties
author.datasource.jdbcUrl=jdbc:mysql://root:12345678@localhost:3306/author
author.datasource.driverClassName=com.mysql.cj.jdbc.Driver

book.datasource.jdbcUrl=jdbc:mysql://root:12345678@localhost:3306/book
book.datasource.driverClassName=com.mysql.cj.jdbc.Driver
Next step, if you're using in your project's dependencies, you need exclude EzyJpaConfiguration from bean management like this:
import com.tvd12.ezyfox.bean.annotation.EzyExclusiveClassesConfiguration;
import com.tvd12.ezyfox.boot.jpa.EzyJpaConfiguration;

@EzyExclusiveClassesConfiguration(
    EzyJpaConfiguration.class
)
public class ExampleJpaMultiDatasourceApplication {
    public static void main(String[] args) throws Exception {
        EzyHttpApplicationBootstrap.start(ExampleJpaMultiDatasourceApplication.class);
    }
}
Now, you can create 2 configration classes like this:
import static com.tvd12.properties.file.util.PropertiesUtil.getPropertiesByPrefix;
import static java.util.Collections.singleton;

import java.util.Properties;
import java.util.Set;

import com.tvd12.ezyfox.bean.annotation.EzyConfigurationBefore;
import com.tvd12.ezyfox.boot.jpa.EzyJpaConfiguration;

@EzyConfigurationBefore
public class AuthorJpaConfiguration extends EzyJpaConfiguration {

    @Override
    public void setPackagesToScan(Set<String> packagesToScan) {
        super.setPackagesToScan(
            singleton("com.tvd12.ezydata.example.jpa.author")
        );
    }

    @Override
    public void setProperties(Properties properties) {
        final Properties clone = new Properties(properties);
        clone.putAll(getPropertiesByPrefix(properties, "author"));
        super.setProperties(clone);
    }
}
import static com.tvd12.properties.file.util.PropertiesUtil.getPropertiesByPrefix;
import static java.util.Collections.singleton;

import java.util.Properties;
import java.util.Set;

import com.tvd12.ezyfox.bean.annotation.EzyConfigurationBefore;
import com.tvd12.ezyfox.boot.jpa.EzyJpaConfiguration;

@EzyConfigurationBefore
public class BookJpaConfiguration extends EzyJpaConfiguration {

    @Override
    public void setPackagesToScan(Set<String> packagesToScan) {
        super.setPackagesToScan(
            singleton("com.tvd12.ezydata.example.jpa.book")
        );
    }

    @Override
    public void setProperties(Properties properties) {
        final Properties clone = new Properties(properties);
        clone.putAll(getPropertiesByPrefix(properties, "book"));
        super.setProperties(clone);
    }
}
For entity classes and repository interfaces, you will need to organize them into the respective packages like this:
root-package/

  +--config/
  |  +--AuthorJpaConfiguration.java
  |  +--BookJpaConfiguration.java
  +--author/
  |  +--repository/
  |  |  +--AuthorRepository.java
  |  +--entity/
  |    +--Author.java
  +--book/
  |  +--repository/
  |  |  +--BookRepository.java
  |  |  +--CategoryRepository.java
  |  +--result/
  |  |  +--SumBookPriceResult.java
  |  +--entity/
  |    +--Category.java
  |    +--Book.java

2. Usage

After the configuration, you can use the repositories normally like single data source like this:
@EzySingleton
@AllArgsConstructor
public class BookService {
    private final AuthorRepository authorRepository;
    private final BookRepository bookRepository;

    public BookData addBook(AddBookData data) {
        Book existedBook = bookRepository.findByNameAndAuthorId(
            data.getBookName(),
            data.getAuthorId()
        );
        if (existedBook != null) {
            throw new DuplicatedBookException(
                "author: " + data.getAuthorId() +
                    " has already registered book: " + data.getBookName()
            );
        }
        final Author author = authorRepository.findById(data.getAuthorId());
        if (author == null) {
            throw new InvalidAuthorIdException(
                "author: " + data.getAuthorId() + " not found"
            );
        }
        // other codes
    }
}