본문 바로가기
DEV/Spring Boot

Spring Boot 1개에 DB 2개 연결?

by wooki4307 2021. 1. 23.

말 그대로다. 

 

회사에서 프로젝트 개발을 진행하는 중에 진행 중인 프로젝트의 메인은 MariaDB, 참조로 Oracle을 연동시켜서 데이터 확인해야 했다.

 

개발에서 안되는게 있겠나... 싶었는데 역시나 없었다.

 

Spring 에서 제공하는 참조 문서를 찾아 봤는데 이러한 문서가 있었다.

 

www.baeldung.com/spring-data-jpa-multiple-databases

 

Spring JPA – Multiple Databases | Baeldung

How to set up Spring Data JPA to work with multiple, separate databases.

www.baeldung.com

그냥 이거대로 하면 완성이 된다.

 

굳이! 내가 했던 뻘짓들을 되돌아 보며 반성의 시간을 가져보려고 합니다.

 

xml로 짜면 되지만 굳~~이 내가 java로 config설정한 이유는... 내가 한 프로젝트는 계속 java로 세팅해서... 다른 이유는 없다^^;

 

프로세스 흐름도

위에 Sub와 Main을 정한 이유가 있습니다. 

 

이글을 보시는 분들도 Sub와 Main을 미리 정해두세요!

 

그다음 Config java를 만들어 줍니다.

 

저는 MariaDataSourceConfig / OracleDataSourceConfig 이렇게 나누었어요!

@Configuration                                           // Configuration 클래스임을 명시
@PropertySource({"classpath:'프로퍼티 파일명'"})             // 해당 Configuration 클래스가 읽을 properties 명시
@EnableJpaRepositories(
        basePackages = "해당 DataSource를 적용할 패키지 경로",      // 해당 패키지가 적용될 repository
        entityManagerFactoryRef = "entityManagerFactory명",   // 해당 TransactionEntityManagerFactory가 사용할 메소드명
        transactionManagerRef = "transactionManager명"        // 해당 TransactinoManager가 참조할 메소드 명
)

@PropertySource에서 해당 Configuration을 적용할 프로퍼티 명을 적어주는데, 기본적으로 Main이 되는 Properties를 참조하게 됩니다.

 

저같은 경우는 내부 프로젝트가 여러개라... Oracle properties 와 Maria properties를 나누었습니다. 

 

제가 운영까지 같이 해야하기 때문에 ^^

 

entityManagerFactory, transactionManager Bean의 이름이 될 것들이니 정성껏 만들어 주세요.

 

basePackages 경로는 저같은 경우 Oracle이 접근하는 repository 패키지와 maria가 접근하는 패키지 경로를 나누었기 때문에

 

해당 패키지 경로를 basePackages로 지정해줬습니다.

 

그 다음 선택적으로 선언해주셔야 하는 의존성이 있는데 

 

@Autowired
private Environment environment;

 

Environment입니다. 설정값 중 별도로 지정해줘야 하는 부분이 있다면 세팅해서 넣어주셔야 하기때문에 선택옵션이예요!

 

@ConfigurationProperties 는 하나의 properties를 사용하시는 분들이라면 필요하지만 두개의 properties를 사용하시는 분들은

 

기본값을 사용하셔도 됩니다. 

 

저는 그냥... 2개의 properties를 사용했지만 소스에서 알아보려고 넣어놓은 거예요.

 

여기서 @Primary를 써줬는데 해당 어노테이션은 단순히 'Maria가 우선이야!' 라고 하는 거예요.

 

해당 값이 없다면 에러의 내용은 의역하자면 다음과 같이 날거예요.

 

'너 DataSource 2개인데? 하나만 설정해야하는 거야 멍청아!'

 

컴퓨터에게 '미쳤습니까 휴먼?' 소리 듣지 않기위해 넣어주세요. 그래도 에러 내용을 자세히 보고싶으시면 빼고 넣으시면 바로 보여요 ㅎㅎ

 

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

 

그 다음 entityManagerFactory 메소드를 지정해줘야합니다.

 

@Primary
@Bean
  public LocalContainerEntityManagerFactoryBean mariaEntityManager() {
  final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
  em.setDataSource(mariaDataSource());
  // EntityManager가 사용할 엔티티 패키지 경로
  em.setPackagesToScan("엔티티 패키지 경로");

  final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
  em.setJpaVendorAdapter(vendorAdapter);
  final HashMap<String, Object> properties = new HashMap<String, Object>();
  // hibernate에 지정할 속성값
  properties.put("hibernate.show_sql",environment.getProperty("spring.maria.jpa.show-sql"));
  properties.put("hibernate.dialect",environment.getProperty("spring.maria.jpa.properties.hibernate.dialect"));
  em.setJpaPropertyMap(properties);
  return em;
}

엔티티 패키지 경로는 DB 조회 후 '데이터 어느 인스턴스에다가 넣어줄까?' 라는 것과 비슷합니다.

 

그 인스턴스 경로를 넣어주시면 됩니다.

 

여기서 environment를 사용했는데 프로퍼티에 설정한 값을 DataSource 기준으로 넣어줬습니다. 

 

show-sql과 dialect를 넣어줬어요.

 

마지막으로 transactionManager를 만들어 주면 끝!

 

@Primary
@Bean
public PlatformTransactionManager mariaTransactionManager() {
  final JpaTransactionManager transactionManager = new JpaTransactionManager();
  transactionManager.setEntityManagerFactory(mariaEntityManager().getObject());
  return transactionManager;
}

이제 이 소스를 Ctrl+C를 해주시고 다른 Config파일에서 Ctrl+V를 해주시면 되요!

 

물론 여기 소스에서 한글로 된 부분만 새로운 값에 맞게 변경해주세요!

 

전체 소스도 같이 첨부해 드릴게요! 참고하시고 한글만 바꾸시면 됩니다.

 

주의하실 점은 @Primary는 꼭 빼주세요!

 

@Primary가 2개면 안되기 때문입니다!

 

모두 즐거운 개발하세요!

 

package 패키지_경로

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

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

/** Maria DB DataSource **/
/**   > 애매하면 다 MariaDataSource를 참고하게 됨 **/

@Configuration                                                          // Configuration 클래스임을 명시
@PropertySource({"classpath:참조할 DB 프로퍼티 이름.properties"})            // 해당 Configuration 클래스가 읽을 properties 명시
@EnableJpaRepositories(
        basePackages = "해당 DataSource를 사용할 패키지 풀경로", 			   // 해당 패키지가 적용될 repository
        entityManagerFactoryRef = "엔티티_매니저_Bean_이름",                 // 해당 TransactionEntityManagerFactory가 사용할 메소드명
        transactionManagerRef = "트랜잭션_매니저_Bean_이름"               // 해당 TransactinoManager가 참조할 메소드 명
)
public class MariaDataSourceConfig{

    @Autowired
    private Environment environment;

    public MariaDataSourceConfig(){
        super();
    }

    // @Primary : 우선적으로 적용될 Bean임을 명시 -> 유니크 해야할 TransactionManager가 중복으로 읽어들이기 때문에 MariaDB를 우선으로 지정한다.
    @Primary
    @Bean
    public LocalContainerEntityManagerFactoryBean 엔티티_매니저_Bean_이름() {
        final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(데이터_소스_생성자());
        // EntityManager가 사용할 엔티티 패키지 경로
        em.setPackagesToScan("적용될 instance 패키지 경로");

        final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        final HashMap<String, Object> properties = new HashMap<String, Object>();
        // hibernate에 지정할 속성값
        properties.put("hibernate.show_sql",environment.getProperty("properties 설정한 Key값"));
        properties.put("hibernate.dialect",environment.getProperty("properties 설정한 Key값"));
        em.setJpaPropertyMap(properties);
        return em;
    }

    @Primary
    @Bean
    @ConfigurationProperties(prefix="프로퍼티의 prefix")
    public DataSource 데이터_소스_생성자() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean
    public PlatformTransactionManager 트랜잭션_매니저_Bean_이름() {
        final JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(엔티티_매니저_Bean_이름().getObject());
        return transactionManager;
    }
}

 

 

-- 6줄요약 --

1. Main과 Sub DataSource를 지정한다.

2. properties를 만들고 안에 데이터 값을 다 넣어준다.

3. 위의 소스를 Ctrl+C를 하고 하나의 DataSourceConfig 파일에 Ctrl+V를 한다.

4. 다른 하나의 DataSourceConfig 파일에 Ctrl+V를 한번 더 한다.

5. Sub가 되는 DataSourceConfig java에 @Primary를 뺀다.

6. 실행한다.

 

'DEV > Spring Boot' 카테고리의 다른 글

Spring Boot Batch - MongoDB 연동하기  (0) 2020.12.06
Spring Boot - MongoDB 연동하기  (0) 2020.11.29
Spring - Batch(배치)  (0) 2020.02.12