EzyJPA Connect Multi-Datasources

Updated at 1685687484000
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 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 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
        }
    }