다중 DB(데이터베이스) 연결

Backend kwon·2023년 10월 9일

본 포스팅은 프로젝트 완료 후, To do로 남겨둔 회원 탈퇴 이관과 관련한 작업을 위해서 다중 데이터베이스 연결과 관련된 포스팅으로 실제 회원 탈퇴와 관련된 정책은 회사마다 다르므로 실제 이관 작업은 생략하고, 다중 데이터베이스를 연결하는 것에 초점을 맞췄습니다.

⚫다중 연결

스프링 부트에서는 하나의 데이터베이스에 대해서만 application.properties 변수 설정을 통해 연결이 가능하기 때문에 2개 이상부터는 Config 클래스 작성을 통해서만 연결이 가능합니다.

단순히 application.properties 파일에 2개의 DB 소스를 줄 경우 오류가 발생하고, Config 클래스를 통해 이를 해결해야 합니다.

(이번 포스팅에서는 MY SQL의 데이터베이스 2개를 통해 실습을 진행합니다.)

MYSQL은 내부에 독립적인 DB공간을 제공하기 때문에 내부에 DB 2개를 생성하여 진행합니다.

 

👍프로젝트 필수 의존성 추가

  • MYSQL Driver
  • Spring Data JPA

 

⚫데이터베이스별 패키지 생성


각각의 데이터베이스에 해당하는 Entity와 repository를 담을 패키지를 생성합니다.

 

⚫DB별 Config 클래스 작성

연결할 데이터베이스의 개수 만큼 Config 클래스를 작성하면 됩니다.

  • first
package com.example.testdatabase.config;

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.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;

@Configuration
@EnableJpaRepositories(
        basePackages = "com.example.testdatabase.firstdb.repository",
        entityManagerFactoryRef = "firstEntityManger",
        transactionManagerRef = "firstTransactionManager"
)
public class FirstDatabaseConfig {

    @Primary
    @Bean
    public PlatformTransactionManager firstTransactionManager() {

        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(firstEntityManger().getObject());

        return transactionManager;
    }

    @Primary
    @Bean
    public LocalContainerEntityManagerFactoryBean firstEntityManger() {

        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();

        em.setDataSource(firstDataSource());
        em.setPackagesToScan(new String[]{"com.example.testdatabase.firstdb.entity"});
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "update");

        em.setJpaPropertyMap(properties);

        return em;
    }

    @Primary
    @Bean
    public DataSource firstDataSource() {

        return DataSourceBuilder.create()
                .driverClassName("com.mysql.cj.jdbc.Driver")
                .url("jdbc:mysql://아이피:3306/디비?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true")
                .username("")
                .password("")
                .build();
    }
}

코드와 관련된 설명을 잠깐 해보자면,

일단 @Configuration으로 설정파일임을 명시하고,
@EnableJpaRepositories를 통해 JpaRepository를 활성화 합니다.

@EnableJpaRepositories의

  • basePackages는 JPA 리포지토리 인터페이스가 위치한 패키지를 나타냅니다.
  • entityManagerFactoryRef는 EntityManagerFactory 빈의 이름을 지정합니다. 이 빈은 데이터베이스와 연결됩니다.
  • transactionManagerRef는 데이터 소스에 대한 트랜잭션 관리자 빈의 이름을 지정합니다. 이 빈은 데이터베이스 트랜잭션을 관리합니다.

즉, 각 데이터 소스에 대한 EntityManagerFactory와PlatformTransactionManager를 직접 설정해주어야 하는 것입니다.

직접 설정한 firstEntityManger()를 보면

em.setDataSource(firstDataSource()); //데이터 소스 set
em.setPackagesToScan(new String[]{"com.example.testdatabase.firstdb.entity"}); // entity를 스캔할 패키지를 설정합니다. 이것은 string 배열 값으로 들어갑니다.
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());  // 어댑터 설정
//ddl autp 설정을 map에 넣어서 entitiymanger의 jpapropertymap의 넣어줍니다.
HashMap<String, Object> properties = new HashMap<>(); 
properties.put("hibernate.hbm2ddl.auto", "update");

em.setJpaPropertyMap(properties);

이렇게 됩니다.

그 다음 firstTransactionManager()를 보면

paTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(firstEntityManger().getObject()); //위에서 설정한 entitymanger값을 넣어줍니다.

return transactionManager;

다음 firstDataSource()를 보면

@Primary
@Bean
public DataSource firstDataSource() {

        //builder패턴을 통해  datasource를 만들어 관련된 설정값들을 지정해 줍니다.
        return DataSourceBuilder.create()
                .driverClassName("com.mysql.cj.jdbc.Driver")
                .url("jdbc:mysql://아이피:3306/디비?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true")
                .username("")
                .password("")
                .build();
    }

여기서 실행을 시키면 오류가 납니다.
second의 config와는 다르게 first에는 오류가 나지 않기 위해 @Primary 어노테이션을 각각의 빈에 적어줍니다.

 

  • second
package com.example.testdatabase.config;

import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

@Configuration
@EnableJpaRepositories(
        basePackages = "com.example.testdatabase.seconddb.repository",
        entityManagerFactoryRef = "secondEntityManager",
        transactionManagerRef = "secondTransactionManager"
)
public class SecondDatabaseConfig {

    @Bean
    public PlatformTransactionManager secondTransactionManager() {

        JpaTransactionManager transactionManager = new JpaTransactionManager();

        transactionManager.setEntityManagerFactory(secondEntityManager().getObject());

        return transactionManager;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean secondEntityManager() {

        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();

        em.setDataSource(secondDataSource());
        em.setPackagesToScan(new String[]{"com.example.testdatabase.seconddb.entity"});
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "update");

        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    public DataSource secondDataSource() {

        return DataSourceBuilder.create()
                .driverClassName("com.mysql.cj.jdbc.Driver")
                .url("jdbc:mysql://아이피:3306/디비?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true")
                .username("")
                .password("")
                .build();
    }
}

second 또한 first config와 동일한 코드이므로 설명은 생략합니다.

profile
백엔드개발자를 향해서

0개의 댓글