Programming/Spring Boot

Spring Boot 다중 데이터베이스 구성하는 방법

Jan92 2023. 12. 5. 10:29

Spring Boot 다중 데이터베이스(Multi Datasource) 구성하는 방법

 

spring boot multi datasource

해당 포스팅은 '스프링 부트 환경에서 다중 데이버테이스 연결을 구성하는 방법'에 대한 내용입니다.

단순히 다중 데이터베이스 연결을 구성하는 방법은 어렵지 않지만 구성 후 다중 데이버테이스에 대한 트랜잭션 처리 등 함께 공부할만한 내용이 있어 정리하게 되었으며, TransactionManager가 여러 개일 때 트랜잭션 처리에 대해서는 해당 포스팅이 아닌 이어지는 포스팅에서 다룰 예정이니 참고 부탁드리겠습니다.

 


Multi Datasource (application.yml)

다중 데이터베이스를 연결하여 사용하는 방법의 가장 핵심은 '각각의 데이터베이스에 대한 Datasource를 구성하는 것'인데요.

 

먼저 application.yml 파일에 각각의 database 연결 정보를 설정하고, 이어서 각 데이터베이스에 대한 DatasourceConfig 파일을 생성하는 순서로 내용을 살펴보겠습니다.

 

spring:
  first-datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://localhost:3306/first_database
    username:
    password:

  second-datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://localhost:3306/second_database
    username:
    password:

(다중 데이터베이스 연결 시 application.yml 파일 중 일부)

 

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/database 
    username:
    password:

(단일 데이터베이스 연결 시 application.yml 파일 중 일부)

 

단일 데이터베이스를 연결할 때와 다중 데이터베이스를 연결할 때 application.yml 파일의 datasource 관련 내용입니다.

 

먼저 단일 데이터베이스에서 'spring.datasource'로 설정하던 부분이 각각의 datasource 정보를 나타내도록 변경되었습니다.

(first-datasource, second-datasource는 예시로 원하는 이름을 지정하고 이후 config 파일에서 해당 부분을 가져오도록 하면 됩니다.)

그리고 주의해야 할 부분이 바로 'spring.datasource.jdbc-url'입니다.

만약 'spring.datasource.url'으로 사용하게 되면 아래와 같은 오류가 발생하는데요.

 

java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.

 

***

이유는 자동 설정의 경우에는 'spring.datasource.url'이 Datasource의 url이 되지만, 지금 경우와 같이 Java Config를 통한 수동 설정을 하는 경우 'spring.datasource.jdbc-url'로 입력해야 HikariCP가 인식을 하기 때문입니다.

 

 


Multi Datasource (DatasourceConfig)

@EnableJpaRepositories(
        basePackages = "com.example.multidatabase.first",
        entityManagerFactoryRef = "firstDatabaseEntityManagerFactory",
        transactionManagerRef = "firstDatabaseTransactionManager"
)
@Configuration
public class FirstDatasourceConfig {

    @Primary
    @Bean
    public LocalContainerEntityManagerFactoryBean firstDatabaseEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(FirstDatabaseDataSource());
        em.setPackagesToScan("com.example.multidatabase.first.entity");
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        return em;
    }

    @Primary
    @Bean
    @ConfigurationProperties(prefix = "spring.first-datasource")
    public DataSource FirstDatabaseDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean
    public PlatformTransactionManager firstDatabaseTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(firstDatabaseEntityManagerFactory().getObject());
        return transactionManager;
    }
}

(FitstDatasourceConfig)

 

이어서 첫 번째 데이터베이스에 대한 DatasourceConfig입니다.

 

여기서 중요하게 봐야 할 부분 중 하나는 @EnableJpaRepositories 어노테이션의 속성인데요.

(@EnableJpaRepositories는 JPA Repository를 활성화하기 위한 어노테이션)

 

basePackages를 통해 활성화할 repository의 패키지 경로(첫 번째 데이터베이스 관련 repository 패키지)를 설정하고, entityManagerFactoryRef, transactionManagerRef 속성을 통해 entityManagerFactory와 transactionManager를 지정해주어야 합니다.

 

***

transactionManager에 대한 default bean 이름이 transactionManager이며, entityManagerFactory 또한 default bean 이름이 entityManagerFactory로 되어 있기 때문에 해당 config 클래스에서 bean으로 등록한 entityManagerFactory, transcationManager의 이름을 명시해 줘야 해당 bean을 찾아올 수 있습니다.

(명시하지 않으면 bean을 찾을 수 없다는 오류가 발생합니다.)

 

 

두 번째로 중요한 부분은 바로 '@Primary' 어노테이션인데요.

 Multi Datasource 구성을 위해서는 위에서 살펴본 DatasourceConfig 외에 각각의 데이터베이스에 대한 DatasourceConfig를 생성해주어야 합니다.

 

@Primary 어노테이션을 사용하지 않으면 각각의 DatasourceConfig 빈으로 등록한 datasource, transactionManager, entityManagerFactory가 충돌하게 됩니다.

따라서 @Primary 어노테이션을 통해 bean에 대한 우선순위를 지정해 줄 필요가 있습니다.

 

 

@EnableJpaRepositories(
    basePackages = "com.example.multidatabase.second",
    entityManagerFactoryRef = "SecondDatabaseEntityManagerFactory",
    transactionManagerRef = "SecondDatabaseTransactionManager"
)
@Configuration
public class SecondDatasourceConfig {

    @Bean
    public LocalContainerEntityManagerFactoryBean SecondDatabaseEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(SecondDatabaseDataSource());
        em.setPackagesToScan("com.example.multidatabase.second.entity");
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        return em;
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.second-datasource")
    public DataSource SecondDatabaseDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public PlatformTransactionManager SecondDatabaseTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(SecondDatabaseEntityManagerFactory().getObject());
        return transactionManager;
    }
}

(SecondDatasourceConfig)

 

이어서 두 번째 데이터베이스에 대한 DatasourceConfig입니다.

첫 번째 DatasourceConfig와 다른 점은 스캔할 패키지 경로 및 bean 이름 그리고 @Primary 어노테이션이 빠졌다는 것입니다.

 

 


다중 데이터베이스 연결 Test

multi datasource test

@DataJpaTest를 통한 각각의 데이터베이스가 잘 연결되었는지 테스트하였으며, 각각의 datasource에서 등록한 bean을 스캔하기 위해 test 패키지에 config 파일을 등록하고 @Import 하였습니다.

 

추가적으로 @AutoConfigureTestDatabase 어노테이션의 속성 값을 NONE으로 지정하여 자동 구성된 Test Database가 Datasource Bean을 대체하지 않도록 설정하였습니다.

 

여기까지 스프링 부트 환경에서 다중 데이터베이스를 연결하는 방법에 대해 살펴봤으며, 이어지는 포스팅을 통해 다음과 같이 TransactionManager가 여러 개일 때 트랜잭션 처리는 어떻게 되는지 살펴보겠습니다.

 

 

 

< github 소스 코드>

https://github.com/JianChoi-Kor/multidatabase