Spring Boot JPA 멀티 데이타소스

By | 2021년 7월 20일
Table of Content

Spring Boot JPA 멀티 데이타소스

목표

두개 이상의 데이타소스를 설정합니다.

application.yml

기존 하나의 데이타소스 설정에서 두개 이상의 데이타소스를 설정할 수 있습니다.
url 설정과 jdbc-url 이 필요합니다.

두개의 데이타소스는 아이피가 달라도 되고, 데이타베이스의 종류가 달라도 됩니다.

spring:
#  datasource:
#    url: jdbc:sqlserver://XXX.XXX.XXX.XXX;databaseName=db_order
#    # url: jdbc:log4jdbc:sqlserver://XXX.XXX.XXX.XXX;databaseName=db_order
#    username: -
#    password: -
#    driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
#    # driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
  datasource:
    db-main:
      url: jdbc:sqlserver://XXX.XXX.XXX.XXX;databaseName=db_order
      jdbc-url: jdbc:sqlserver://XXX.XXX.XXX.XXX;databaseName=db_order
      username: -
      password: -
      driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
    db-item:
      url: jdbc:sqlserver://XXX.XXX.XXX.XXX;databaseName=db_item
      jdbc-url: jdbc:sqlserver://XXX.XXX.XXX.XXX;databaseName=db_item
      username: -
      password: -
      driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver

DataSourceProperties.java

데이타소스를 읽는 설정을 추가합니다.

@Configuration
@EnableConfigurationProperties
public class DataSourceProperties {

    @Bean(name = "mainDataSource")
    @Qualifier("mainDataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.db-main")
    public DataSource mainDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name = "itemDataSource")
    @Qualifier("itemDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db-item")
    public DataSource itemDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }
}

MainDataSourceConfig.java

패키지 기준으로 데이타소스가 할당됩니다.

따라서 Entity/Repository 클래스는 데이타소스별로 분리해서 배치해야 합니다.

중요 데이타소스 쪽에 @Primary 를 붙여줍니다.

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "mainEntityManagerFactory",
        transactionManagerRef = "mainTransactionManager",
        basePackages = { "kr.pe.skyer9.csapi.domain.db_main" })
public class MainDataSourceConfig {

    @Autowired
    @Qualifier("mainDataSource")
    private DataSource mainDataSource;

    @Primary
    @Bean(name = "mainEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean mainEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(mainDataSource)
                .packages("kr.pe.skyer9.csapi.domain.db_main")
                .persistenceUnit("main")
                .build();
    }

    @Primary
    @Bean("mainTransactionManager")
    public PlatformTransactionManager mainTransactionManager(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(mainEntityManagerFactory(builder).getObject());
    }
}

itemDataSourceConfig.java

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "itemEntityManagerFactory",
        transactionManagerRef = "itemTransactionManager",
        basePackages = { "kr.pe.skyer9.csapi.domain.db_item" })
public class itemDataSourceConfig {

    @Autowired
    @Qualifier("itemDataSource")
    private DataSource itemDataSource;

    @Bean(name = "itemEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean itemEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(itemDataSource)
                .packages("kr.pe.skyer9.csapi.domain.db_item")
                .persistenceUnit("item")
                .build();
    }

    @Bean("itemTransactionManager")
    public PlatformTransactionManager itemTransactionManager(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(itemEntityManagerFactory(builder).getObject());
    }
}

Service 레이어

가끔 프로시져 호출 등의 이유로 서비스 레이어에서 EntityManager 를 생성하는 경우가 있습니다.

이런 경우 @RequiredArgsConstructor 를 사용할 수 없고,
@Qualifier("mainEntityManagerFactory") 와 같이 Bean 을 명시해 주어야 정상 작동합니다.

// @RequiredArgsConstructor
@Service
public class OrderMasterService {

    private final OrderMasterMapper mapper = Mappers.getMapper(OrderMasterMapper.class);
    private final OrderMasterRepository orderMasterRepository;

    private final EntityManager entityManager;

    public OrderMasterService(OrderMasterRepository orderMasterRepository, @Qualifier("mainEntityManagerFactory") EntityManager entityManager) {
        this.orderMasterRepository = orderMasterRepository;
        this.entityManager = entityManager;
    }
}

답글 남기기