Spring 다중 DB 연결 구현

Donghyun Kim·2023년 7월 20일
0

구성환경

  • SpringBoot 2.7.4

application.yml

spring:
  datasource: # config/db의 config 클래스를 통해 db 환경설정
    hikari:
      main: # Main Application DB 정보
        driver-class-name: org.mariadb.jdbc.Driver
        jdbc-url: jdbc:mariadb:생략
        username: name
        password: password
        maximum-pool-size: 30
      meta: # Meta Data DB 정보
        driver-class-name: org.postgresql.Driver
        jdbc-url: jdbc:postgresql://생략
        username: name
        password: password
        maximum-pool-size: 30

위와 같이 main, meta 등으로 사용될 DB 별 별칭을 주어 구분하고
각각 properties를 작성합니다. (이후 해당 정보를 불러와 스프링 빈을 통해 Datasource Config에 주입)

기존 단일 DB 연결을 위한 datasource 설정 시엔 url로 작성하였으나 jdbcUrl로 벼견해서 작성해야한다. JavaConfig를 통해 사용자가 직접 커스텀하여 datasource를 구성할 경우 HikariCP가 Url이 아닌 jdbcUrl을 사용하기 때문.

정리하자면,

  • yaml에 바로 datasource 정보 입력
    -> url
  • datasource config 클래스 구현을 통한 datasource 정보 입력
    -> jdbc-url
  jpa:
    hibernate:
      main:
        ddl-auto: create
        naming: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
        show_sql: true
        format_sql: true
        use_sql_comments: true
        generate-ddl: false
      meta:
        ddl-auto: none
        naming: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
        show_sql: true
        format_sql: true
        use_sql_comments: true
        generate-ddl: false

JPA Hibernate 설정 정보도 DB 별로 다르게 만들어 하위 작성될 Datasource Config 설정을 통해 각각 설정해줄 수 있다.

MainDatasourceConfig.java

@EnableJpaRepositories(
    basePackages = "symphony.api",
    entityManagerFactoryRef = "mainEntityManagerFactoryBean", // LocalContainerEntityManagerFactoryBean
    transactionManagerRef = "mainTransactionManager" // PlatformTransactionManager
)
@Configuration
/** sym-dashboard-api db 설정 정보 **/
public class MainDataSourceConfig {

    private final String mainEntityRootPackage = "symphony.api";

    @Value("${spring.jpa.hibernate.main.ddl-auto}")
    private String jpaDdlAuto;
    @Value("${spring.jpa.hibernate.main.naming}")
    private String jpaNamingPhysicalStrategy;
    @Value("${spring.jpa.hibernate.main.show_sql}")
    private String jpaShowSql;
    @Value("${spring.jpa.hibernate.main.format_sql}")
    private String jpaFormatSql;
    @Value("${spring.jpa.hibernate.main.use_sql_comments}")
    private String jpaUseSqlComments;
    @Value("${spring.jpa.hibernate.main.generate-ddl}")
    private String jpaGenerateDdl;

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.hikari.main")
    public DataSource mainDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean mainEntityManagerFactoryBean() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();

        em.setDataSource(mainDataSource()); // DataSource 설정
        em.setPackagesToScan(mainEntityRootPackage); // 연결 Entity 패키지 경로 설정
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        // Properties 설정
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", jpaDdlAuto);
        properties.put("hibernate.naming.physical-strategy", jpaNamingPhysicalStrategy);
        properties.put("hibernate.show_sql", jpaShowSql);
        properties.put("hibernate.format_sql", jpaFormatSql);
        properties.put("hibernate.use_sql_comments", jpaUseSqlComments);
        properties.put("generate-ddl", jpaGenerateDdl);
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    @Primary
    public PlatformTransactionManager mainTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(mainEntityManagerFactoryBean().getObject());

        return transactionManager;
    }

MainDatasourceConfig.java

@EnableJpaRepositories(
        basePackages = "symphony.metamanager",
        entityManagerFactoryRef = "metaEntityManagerFactoryBean", // LocalContainerEntityManagerFactoryBean
        transactionManagerRef = "metaTransactionManager" // PlatformTransactionManager
)
@Configuration
/** sym-meta-manager db 설정 정보 **/
public class MetaDataSourceConfig {

    private final String metaEntityRootPackage = "symphony.metamanager.model";

    @Value("${spring.jpa.hibernate.meta.ddl-auto}")
    private String jpaDdlAuto;
    @Value("${spring.jpa.hibernate.meta.naming}")
    private String jpaNamingPhysicalStrategy;
    @Value("${spring.jpa.hibernate.meta.show_sql}")
    private String jpaShowSql;
    @Value("${spring.jpa.hibernate.meta.format_sql}")
    private String jpaFormatSql;
    @Value("${spring.jpa.hibernate.meta.use_sql_comments}")
    private String jpaUseSqlComments;
    @Value("${spring.jpa.hibernate.meta.generate-ddl}")
    private String jpaGenerateDdl;

    @Bean
    @ConfigurationProperties("spring.datasource.hikari.meta")
    public DataSource metaDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean metaEntityManagerFactoryBean() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();

        em.setDataSource(metaDataSource()); // DataSource 설정
        em.setPackagesToScan(metaEntityRootPackage); // 연결 Entity 패키지 경로 설정
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        // Properties 설정
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", jpaDdlAuto);
        properties.put("hibernate.naming.physical-strategy", jpaNamingPhysicalStrategy);
        properties.put("hibernate.show_sql", jpaShowSql);
        properties.put("hibernate.format_sql", jpaFormatSql);
        properties.put("hibernate.use_sql_comments", jpaUseSqlComments);
        properties.put("generate-ddl", jpaGenerateDdl);
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    public PlatformTransactionManager metaTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(metaEntityManagerFactoryBean().getObject());

        return transactionManager;
    }
}

코드 설명

멤버 변수

  • @Vaulue 어노테이션을 통해 application.yml에 작성된 프로퍼티를 읽어와 변수로 할당하고 Bean에 세팅한다.

@EnableJpaRepositories

JPA Repository Bean을 활성화하는 어노테이션

  • basePackages : 적용 할 repository 패키지 경로 작성
    작성하지 않으면 @SpringBootApplication 전역 스캔
  • entityManagerFactoryRef : 참조할 EntityManager Bean 이름 작성
  • trasactionManagerRef : 참조할 TransactionManager Bean 이름 작성

Datasource

사용자 정의 데이터 소스를 설정하기 위한 구현체

LocalContainerEntityManagerFactoryBean

데이터 소스에 설정할 EntityManager 구현체

  • Datasource 객체 할당
  • Entity 스캔 패키지 경로 설정
  • JPA Hibernate 동작을 지정하는 JpaVendorAdapter 설정
  • HashMap을 통해 설정할 Hibernate 값들을 Map에 담아 JpaPropertyMap에 할당

PlatformTransactionManager

JPA 트랜잭션 매니저를 설정하기 위한 구현체

  • 작성한 LocalContainerEntityManagerFactoryBean 인스턴스를 setEntityManagerFactory의 파라미터로 할당하여 구현
profile
"Hello World"

1개의 댓글

comment-user-thumbnail
2023년 7월 20일

가치 있는 정보 공유해주셔서 감사합니다.

답글 달기