Khi chỉ có một cơ sở dữ liệu thì spring boot đã hỗ trợ chúng ta khởi tạo các repository một cách tự động. Tuy nhiên khi ứng dụng của chúng ta cần kết nối từ hai cơ sở dữ liệu trở lên thì lại là vấn đề khác, lúc này spring sẽ khó có thể hỗ trợ chúng ta tự động vậy nên chúng ta sẽ cần phải cấu hình thủ công.

Chuẩn bị

Chúng ta sẽ cần tạo ra hai cơ sở dữ liệu với hai bảng như sau:

CREATE SCHEMA `example1` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin ;
use example1;
CREATE TABLE `users` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

CREATE SCHEMA `example2` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin ;
use example2;
CREATE TABLE `products` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

Sơ đồ lớp của spring jpa

Người sử dụng càng đơn giản bao nhiêu thì ở bên trong spring jpa lại phức tạp bấy nhiêu do có quá nhiều tầng lớp trừu tượng giữa spring, JPA và jdbc, chúng ta có thể bóc tách các lớp phụ thuộc theo sơ đồ dưới đây:

Trong sơ đồ này chúng ta có:

  1. Repository: Là interface mà chúng ta vẫn hay sử dụng để truy cập cơ sở dữ liệu.
  2. JpaRepositoryFactory: Là lớp để tạo ra Repository, đây là một ứng dụng của factory design pattern để che giấu sự phức tạp của việc khởi tạo đối tượng Repository thông qua java byte code.
  3. JpaTransactionManager: Là lớp để quản lý transaction. Nghe thì có vẻ ghê gớm nhưng thực tế lớp này sử dụng EntityManager chứ cũng không cài đặt cơ chế transaction nào phức tạp.
  4. JpaTransactionManagerFactory: Là lớp để tạo ra JpaTransactionManager.
  5. EntityManager: Đây là lớp gần như quan trọng nhất của JPA, ở bên trong lớp cài đặt của Repository sẽ sử dụng lớp này là chính.
  6. EntityManagerFactory: Là lớp để tạo ra các đối tượng EntityManager.
  7. EntityManagerFactoryFactory: Là lớp để tạo ra EntityManagerFactory.
  8. DataSource: Chúng ta đã tìm hiểu lớp này trong các bài trước rồi, đây thực chất cũng là một lớp proxy hoặc adapter khi nó sử dụng đối tượng Conneciton của JDBC.
  9. DataSourceFactory: Là lớp để tạo ra các đối tượng DataSource.
  10. Connection: Là lớp driver kết nối ứng dụng của chúng ta với cơ sở dữ liệu.
  11. ConnectionFactory: Là lớp để tạo ra các đối tượng Connection.
  12. ConnectionPool: Là lớp để quản lý và tái sử dụng Connection.

Khởi tạo dự án

Chúng ta sẽ cần khởi tạo một module có tên spring-boot-jpa-multi-datasources với tập tin pom.xml như sau:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>vn.techmaster</groupId>
        <artifactId>mastering-spring-boot</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>spring-boot-jpa-multi-datasources</artifactId>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>${mysql.version}</version>
        </dependency>
    </dependencies>
</project>

Cấu hình dự án

Chúng ta có thể tạo ra tập tin application.yaml để chứa cấu hình của hai cơ sở dữ liệu như sau:

jpa:
  data-sources:
    example1:
      url: 'jdbc:mysql://localhost:3306/example1'
      username: 'root'
      password: '12345678'
      driver-class-name: 'com.mysql.cj.jdbc.Driver'
      database-platform: 'org.hibernate.dialect.MySQLDialect'
    example2:
      url: 'jdbc:mysql://localhost:3306/example2'
      username: 'root'
      password: '12345678'
      driver-class-name: 'com.mysql.cj.jdbc.Driver'
      database-platform: 'org.hibernate.dialect.MySQLDialect'

Cấu hình qua properties cũng không phải là ý tưởng quá tệ, nhưng cấu hình qua yaml sẽ giúp chúng ta tránh bị trùng lặp các tên thuộc tính gốc kiểu:

jpa.data-sources.example1.url=jdbc:mysql://localhost:3306/example1
# trùng lặp thuộc tính gốc jpa.data-sources.example1
jpa.data-sources.example1.username=root

Mã nguồn của yaml cũng trong sáng hơn.

Mã nguồn cài đặt

Đầu tiên chúng ta sẽ khởi tạo một lớp tiện ích JpaConfigurations như sau:

package vn.techmaster.jpa.config;

import jakarta.persistence.EntityManagerFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

import javax.sql.DataSource;
import java.util.Properties;

public final class JpaConfigurations {
    private JpaConfigurations() {
    }

    public static <D> D createDataSource(
        DataSourceProperties properties,
        Class dataSourceClass
    ) {
        return (D) DataSourceBuilder
            .create()
            .driverClassName(properties.getDriverClassName())
            .url(properties.getUrl())
            .username(properties.getUsername())
            .password(properties.getPassword())
            .type(dataSourceClass)
            .build();
    }

    public static EntityManagerFactory createEntityManagerFactory(
        DataSource dataSource,
        Properties jpaProperties,
        String packageToScan
    ) {
        final LocalContainerEntityManagerFactoryBean factory =
            new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        factory.setPackagesToScan(packageToScan);
        factory.setDataSource(dataSource);
        factory.setJpaProperties(jpaProperties);
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    public static JpaTransactionManager createJpaTransactionManager(
        EntityManagerFactory entityManagerFactory
    ) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

Lớp này có 3 hàm:

  1. createDataSource: Dùng để tạo một đối tượng datasource từ các cấu hình và kiểu được truyền vào. Trong bài này chúng ta sẽ sử dụng HikariDataSource.
  2. createEntityManagerFactory: Hàm này để tạo ra một đối tượng EntityManagerFactory từ đối tượng dataSource các cấu hình của jpa (jpaProperties) và tên gói có chứa các entity.
  3. createJpaTransactionManager: Hàm này để tạo ra một đối tượng JpaTransactionManager với tham số entityManagerFactory truyền vào.
    Tiếp theo chúng ta sẽ tạo một lớp AppDataSourceFactory với mã nguồn như sau:
package vn.techmaster.jpa.datasource;

import com.zaxxer.hikari.HikariDataSource;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.core.env.Environment;
import vn.techmaster.jpa.config.JpaConfigurations;

import javax.sql.DataSource;
import java.util.Map;

@AllArgsConstructor
public class AppDataSourceFactory {
    private final Map<String, DataSourceProperties> dataSourcePropertiesById;
    private final Environment environment;
    private final String hikariDataSourcePropertyPrefix;

    public DataSource createDataSource(String datasourceId) {
        final HikariDataSource dataSource = JpaConfigurations.createDataSource(
            dataSourcePropertiesById.get(datasourceId),
            HikariDataSource.class
        );
        final Binder binder = Binder.get(environment);
        binder.bind(hikariDataSourcePropertyPrefix, Bindable.ofInstance(dataSource));
        return dataSource;
    }
}

Như đã nói trong sơ đồ lớp, lớp AppDataSourceFactory này là một ứng dụng của factory design pattern và nó sẽ có hàm createDataSource để tạo ra một đối tượng DataSource. Ở đây chúng ta truyền vào một datasourceId tương ứng là example1 hoặc example2 trong tập tin cấu hình application.yaml, ở bên trong hàm sẽ lấy ra được cấu hình của datasource tương ứng và khởi tạo.
Tiếp theo chúng ta sẽ khởi tạo lớp AppEntityManagerFactoryFactory với mã nguồn như sau:

package vn.techmaster.jpa.datasource;

import jakarta.persistence.EntityManagerFactory;
import lombok.AllArgsConstructor;
import vn.techmaster.jpa.config.JpaConfigurations;

import javax.sql.DataSource;
import java.util.Properties;

@AllArgsConstructor
public class AppEntityManagerFactoryFactory {

    private final AppDataSourceFactory exampleDataSourceFactory;
    private final Properties hibernateProperties;
    private final String packageToScan;

    public EntityManagerFactory createEntityManagerFactory(String datasourceId) {
        return createEntityManagerFactory(datasourceId, null);
    }

    public EntityManagerFactory createEntityManagerFactory(
        String datasourceId,
        DataSource dataSource
    ) {
        return JpaConfigurations.createEntityManagerFactory(
            dataSource != null ? dataSource : exampleDataSourceFactory.createDataSource(datasourceId),
            hibernateProperties,
            packageToScan
        );
    }
}

Lớp này sẽ có hàm createEntityManagerFactory để khởi tạo ra EntityManagerFactory với tham số truyền vào vẫn là datasourceId và DataSource.
Tiếp theo chúng ta sẽ khởi tạo lớp AppJpaRepositoryFactory với mã nguồn như sau:

package vn.techmaster.jpa.datasource;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import lombok.AllArgsConstructor;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.SharedEntityManagerCreator;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;

@AllArgsConstructor
public class AppJpaRepositoryFactory {
    private final AppJpaTransactionManagerFactory appJpaTransactionManagerFactory;

    public <T, I, R extends JpaRepository> R createJpaRepository(
        String datasourceId,
        Class jpaRepositoryClass,
        JpaTransactionManager jpaTransactionManager
    ) {
        final JpaTransactionManager actualJpaTransactionManager =
            jpaTransactionManager == null
                ? jpaTransactionManager
                : appJpaTransactionManagerFactory.createJpaTransactionManager(datasourceId);
        final EntityManagerFactory entityManagerFactory =
            actualJpaTransactionManager.getEntityManagerFactory();
        assert entityManagerFactory != null;
        final EntityManager entityManager =
            SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
        final JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager);
        final TransactionInterceptor transactionInterceptor = new TransactionInterceptor(
            (TransactionManager) actualJpaTransactionManager,
            new AnnotationTransactionAttributeSource()
        );
        jpaRepositoryFactory.addRepositoryProxyPostProcessor((factory, repositoryInformation) ->
            factory.addAdvice(transactionInterceptor)
        );
        return (R) jpaRepositoryFactory.getRepository(jpaRepositoryClass);
    }
}

Lớp này chứa hàm createJpaRepository để tạo ra các đối tượng Repository cho chúng ta từ datasourceId, lớp hoặc interface của Repository và JpaTransactionManager.
Tiếp theo chúng ta sẽ khởi tạo lớp AppJpaTransactionManagerFactory với mã nguồn như sau:

package vn.techmaster.jpa.datasource;

import jakarta.persistence.EntityManagerFactory;
import lombok.AllArgsConstructor;
import org.springframework.orm.jpa.JpaTransactionManager;
import vn.techmaster.jpa.config.JpaConfigurations;

@AllArgsConstructor
public class AppJpaTransactionManagerFactory {
    private final AppEntityManagerFactoryFactory ezyEntryManagerFactoryFactory;

    public JpaTransactionManager createJpaTransactionManager(String datasourceId) {
        return createJpaTransactionManager(datasourceId, null);
    }

    public JpaTransactionManager createJpaTransactionManager(
        String datasourceId,
        EntityManagerFactory entityManagerFactory
    ) {
        return JpaConfigurations.createJpaTransactionManager(
            entityManagerFactory != null
                ? entityManagerFactory
                : ezyEntryManagerFactoryFactory.createEntityManagerFactory(datasourceId)
        );
    }
}

Lớp này có chứa hàm createJpaTransactionManager để tạo ra một đối tượng JpaTransactionManager tương ứng với datasourceId và entityManagerFactory.
Đã có các lớp thành phần rồi giờ là lúc chúng ta có thể khởi tạo lớp cấu hình AppJpaConfig với mã nguồn như sau:

package vn.techmaster.jpa.config;

import jakarta.persistence.EntityManagerFactory;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import vn.techmaster.jpa.datasource.AppDataSourceFactory;
import vn.techmaster.jpa.datasource.AppEntityManagerFactoryFactory;
import vn.techmaster.jpa.datasource.AppJpaRepositoryFactory;
import vn.techmaster.jpa.datasource.AppJpaTransactionManagerFactory;

import javax.sql.DataSource;
import java.util.Map;
import java.util.Properties;

@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(HibernateProperties.class)
@ImportAutoConfiguration(AppJpaConfig.RepositoryPropertiesConfig.class)
public class AppJpaConfig {

    private final ApplicationContext appContext;

    public static final String EXAMPLE1 = "example1";
    public static final String EXAMPLE2 = "example2";

    @Bean
    public AppDataSourceFactory repositoryDataSourceFactory(
        RepositoryProperties properties
    ) {
        return new AppDataSourceFactory(
            properties.getDataSources(),
            appContext.getEnvironment(),
            "spring.datasource.hikari"
        );
    }

    @Bean
    public AppJpaRepositoryFactory repositoryJpaRepositoryFactory(
        AppJpaTransactionManagerFactory repositoryJpaTransactionManagerFactory
    ) {
        return new AppJpaRepositoryFactory(repositoryJpaTransactionManagerFactory);
    }

    @Bean
    public AppEntityManagerFactoryFactory repositoryEntityManagerFactoryFactory(
        AppDataSourceFactory repositoryDataSourceFactory,
        HibernateProperties hibernateProperties
    ) {
        Properties jpaProperties = new Properties();
        String ddlAuto = hibernateProperties.getDdlAuto();
        if (ddlAuto != null) {
            jpaProperties.setProperty(
                "spring.jpa.hibernate.ddl-auto",
                ddlAuto
            );
        }
        return new AppEntityManagerFactoryFactory(
            repositoryDataSourceFactory,
            jpaProperties,
            "vn.techmaster.jpa.entity"
        );
    }

    @Bean
    public AppJpaTransactionManagerFactory repositoryJpaTransactionManagerFactory(
        AppEntityManagerFactoryFactory repositoryEntityManagerFactoryFactory
    ) {
        return new AppJpaTransactionManagerFactory(repositoryEntityManagerFactoryFactory);
    }

    // ==================== example1 ==========
    @Bean
    public DataSource example1DataSource(AppDataSourceFactory repositoryDataSourceFactory) {
        return repositoryDataSourceFactory.createDataSource(EXAMPLE1);
    }

    @Bean
    public EntityManagerFactory example1EntityManagerFactory(
        @Qualifier("example1DataSource")
        DataSource example1DataSource,
        AppEntityManagerFactoryFactory repositoryEntityManagerFactoryFactory
    ) {
        return repositoryEntityManagerFactoryFactory.createEntityManagerFactory(
            EXAMPLE1,
            example1DataSource
        );
    }

    @Bean
    public JpaTransactionManager example1TransactionManager(
        @Qualifier("example1EntityManagerFactory")
        EntityManagerFactory example1EntityManagerFactory,
        AppJpaTransactionManagerFactory repositoryEntityManagerFactoryFactory
    ) {
        return repositoryEntityManagerFactoryFactory.createJpaTransactionManager(
            EXAMPLE1,
            example1EntityManagerFactory
        );
    }

    // ==================== example2 ==========
    @Bean
    public DataSource example2DataSource(AppDataSourceFactory repositoryDataSourceFactory) {
        return repositoryDataSourceFactory.createDataSource(EXAMPLE2);
    }

    @Bean
    public EntityManagerFactory example2EntityManagerFactory(
        @Qualifier("example2DataSource")
        DataSource example2DataSource,
        AppEntityManagerFactoryFactory repositoryEntityManagerFactoryFactory
    ) {
        return repositoryEntityManagerFactoryFactory.createEntityManagerFactory(
            EXAMPLE2,
            example2DataSource
        );
    }

    @Bean
    public JpaTransactionManager example2TransactionManager(
        @Qualifier("example2EntityManagerFactory")
        EntityManagerFactory example2EntityManagerFactory,
        AppJpaTransactionManagerFactory repositoryEntityManagerFactoryFactory
    ) {
        return repositoryEntityManagerFactoryFactory.createJpaTransactionManager(
            EXAMPLE2,
            example2EntityManagerFactory
        );
    }

    // ================= config ===========

    @Getter
    @Setter
    @ConfigurationProperties(prefix = "jpa")
    public static class RepositoryProperties {
        private Map<String, DataSourceProperties> dataSources;
    }

    @Configuration
    @EnableConfigurationProperties(RepositoryProperties.class)
    @ConditionalOnMissingBean(RepositoryProperties.class)
    public static class RepositoryPropertiesConfig {
    }
}

Ở đây chúng ta đang khởi tạo các cấu hình và các đối tượng cần thiết cho cả hai cơ sở dữ liệu là example1 và example2.
Tiếp theo chúng ta sẽ tạo ra 2 entity tương ứng với hai bảng users và products cho 2 cơ sở dữ liệu với mã nguồn như sau:
Lớp User ánh xạ với bảng users trong cơ sở dữ liệu example1:

package vn.techmaster.jpa.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String name;
}

Lớp Product ánh xạ với bảng products trong cơ sở dữ liệu example2:

package vn.techmaster.jpa.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@Entity
@Table(name = "products")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String name;
}

Tiếp theo chúng ta sẽ tạo ra 2 interface tương ứng cho 2 lớp entity. Đầu tiên là interface UserRepository:

package vn.techmaster.jpa.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import vn.techmaster.jpa.entity.User;

public interface UserRepository extends JpaRepository<User, Long> {}

Sau đó là interface ProductRepository:

package vn.techmaster.jpa.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import vn.techmaster.jpa.entity.Product;

public interface ProductRepository extends JpaRepository<Product, Long> {}

Tiếp theo chúng ta sẽ tạo ra lớp cấu hình Example1RepositoryConfig cho cơ sở dữ liệu example1:

package vn.techmaster.jpa.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import vn.techmaster.jpa.datasource.AppJpaRepositoryFactory;
import vn.techmaster.jpa.repository.UserRepository;

import static vn.techmaster.jpa.config.AppJpaConfig.EXAMPLE1;

@Configuration
public class Example1RepositoryConfig {

    private final AppJpaRepositoryFactory jpaRepositoryFactory;
    private final JpaTransactionManager jpaTransactionManager;

    public Example1RepositoryConfig(
        AppJpaRepositoryFactory jpaRepositoryFactory,
        @Qualifier("example1TransactionManager")
        JpaTransactionManager jpaTransactionManager
    ) {
       this.jpaRepositoryFactory = jpaRepositoryFactory;
       this.jpaTransactionManager = jpaTransactionManager;
    }

    @Bean
    public UserRepository userRepository() {
        return jpaRepositoryFactory.createJpaRepository(
            EXAMPLE1,
            UserRepository.class,
            jpaTransactionManager
        );
    }
}

Lớp Example2RepositoryConfig cho cơ sở dữ liệu example2:

package vn.techmaster.jpa.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import vn.techmaster.jpa.datasource.AppJpaRepositoryFactory;
import vn.techmaster.jpa.repository.ProductRepository;

import static vn.techmaster.jpa.config.AppJpaConfig.EXAMPLE2;

@Configuration
public class Example2RepositoryConfig {

    private final AppJpaRepositoryFactory jpaRepositoryFactory;
    private final JpaTransactionManager jpaTransactionManager;

    public Example2RepositoryConfig(
        AppJpaRepositoryFactory jpaRepositoryFactory,
        @Qualifier("example2TransactionManager")
        JpaTransactionManager jpaTransactionManager
    ) {
       this.jpaRepositoryFactory = jpaRepositoryFactory;
       this.jpaTransactionManager = jpaTransactionManager;
    }

    @Bean
    public ProductRepository productRepository() {
        return jpaRepositoryFactory.createJpaRepository(
            EXAMPLE2,
            ProductRepository.class,
            jpaTransactionManager
        );
    }
}

Bạn cần lưu ý rằng chúng ta cần sử dụng @Qualifier annotation để chỉ định rõ tên bean của JpaTransactionManager tương ứng với cơ sở dữ liệu.

Khởi chạy chương trình

Có lẽ đây là bài viết có nhiều mã nguồn phức tạp và khó hiểu nhất từ đầu loạt bài đến giờ. Tuy nhiên ở khía cạnh người sử dụng thì lại cực kỳ đơn giản, nên dù mã nguồn cấu hình phức tạp một chút cũng đáng. Lẽ ra spring nên đóng gói lại thành một thư viện con sử dụng composite design pattern thì tốt quá, tuy nhiên bạn cũng có thể tạo ra một thư viện sử dụng mã nguồn ở trên để tái sử dụng trong tổ chức của mình.
Để khởi chạy chương trình chúng ta sẽ cần tạo lớp SpringBootJpaMultiDataSourcesStartUp với mã nguồn như sau:

package vn.techmaster.jpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.ApplicationContext;
import vn.techmaster.jpa.entity.Product;
import vn.techmaster.jpa.entity.User;
import vn.techmaster.jpa.repository.ProductRepository;
import vn.techmaster.jpa.repository.UserRepository;

@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class,
    DataSourceTransactionManagerAutoConfiguration.class,
    HibernateJpaAutoConfiguration.class,
    JpaRepositoriesAutoConfiguration.class
})
public class SpringBootJpaMultiDataSourcesStartUp {

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication
            .run(SpringBootJpaMultiDataSourcesStartUp.class);
        UserRepository userRepository = applicationContext.getBean(
            UserRepository.class
        );
        User user = new User();
        user.setName("Techmaster");
        user = userRepository.save(user);
        User fetchedUser = userRepository.findById(user.getId()).get();
        System.out.println("fetchedUser: " + fetchedUser);

        ProductRepository productRepository = applicationContext.getBean(
            ProductRepository.class
        );
        Product product = new Product();
        product.setName("Mastering spring boot");
        product = productRepository.save(product);
        Product fetchedProduct = productRepository.findById(product.getId()).get();
        System.out.println("fetchedProduct: " + fetchedProduct);
    }
}

Khi chạy chương trình kết quả chúng ta nhận được sẽ là:

fetchedUser: User(id=7, name=Techmaster)
fetchedProduct: Product(id=6, name=Mastering spring boot)

Tổng kết

Như vậy chúng ta đã cùng nhau tìm hiểu spring jpa để kết nối nhiều cơ sở dữ liệu, rất xin lỗi bạn vì mã nguồn cài đặt quá phức tạp.


Cám ơn bạn đã quan tâm đến bài viết|video này. Để nhận được thêm các kiến thức bổ ích bạn có thể:

  1. Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
  2. Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
  3. Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a