마이크로서비스의 관점에서, 하나의 DB만 접근을 하고 기능을 최소화 하면 좋겠지만

종류가 다른 여러가지 DB 혹은, 종류가같지만 데이터 분산을 위해 다른 물리DB에

붙어야 되는 케이스가 생깁니다.


데이터 베이스를 나누는 기준은 여러가지가 있으며 다음과 같은 패턴으로 나뉠수 있습니다.

  • 서비스 도메인별로
  • 기능별(중요데이터/로그성)로
  • 역활별로 ( RDB / RDD)


그리고 프로젝트에서 분리반영하여 설정하는 방법은 다음 절차를 따릅니다.

  • application.properties에서 설정을 분리
  • 엔티티정의및 Repository정의 소스 경로 즉 ,JAVA의 package 를 각각 분리결정
  • Configuration 어노테이션을 사용하는 AConfigDB / BConfigDB 클래스를 각각정의

크게 3가지 절차만 잘 수행하면 되겠습니다.

먼저 기존, 하나의 DB만 사용하는 케이스를 살펴봅시다

각 설정을 단계별로 살펴보겠습니다.



1.application.conf 수정

One DB 설정

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.format_sql=true
# For 1 Database
#spring.jpa.hibernate.ddl-auto=create
#spring.datasource.url=jdbc:mysql://localhost:3306/db_example
#spring.datasource.username=psmon
#spring.datasource.password=db1234

 기존에 설정된 ddl-auto설정,접속정보를 우선 제거해야합니다. ddl-auto의 경우 데이터베이스마다 사용전략이

달라지기때문에 관련설정을 제거합니다. 그외에 hiberate 기능은, 별도의 셋팅없이 공통으로 사용가능합니다.

다중 DB설정

# for Multiple Database

## Primary
app.datasource.primary.url=jdbc:postgresql://localhost:5432/db_example
app.datasource.primary.username=postgres
app.datasource.primary.password=db1234
app.datasource.primary.driver-class-name=org.postgresql.Driver
primary.hibernate.hbm2ddl.auto = create
primary.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect


## Second
app.datasource.second.url=jdbc:mysql://localhost:3306/db_example2
app.datasource.second.username=psmon
app.datasource.second.password=db1234
app.datasource.second.driver-class-name=com.mysql.jdbc.Driver
second.hibernate.hbm2ddl.auto = create
second.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

여기서는, db이름을  primary / second 두가지로만 분류하였습니다.

데이터베이스를 나뉘는 기준이 뭐냐에 따라 이름을 달리할수 있으나, 네이밍을 부여하는것은 아주 어려운일입니다.

샘플에서는 primary/second로 각각 두가지 다른 DB ( Postgres / Mysql )를 설정하겠습니다.

JPA가 지원하는 다양한 DB로 설정이 가능하며, 접속정보및 db에 맞는 driver-class-name을 맞추어주어야합니다.


2. 구현 Entity Package경로 분리

DB사용과 관련된, Entity/Repository를 JAVA의 package경로를 결정하고 그 하위에서만

작업을 하면 되겠습니다.  여기서는 단순하게 data / data2 라고 분류를 하였으며

data = primary  , data2 = second 에 각각 연결을 할계획입니다.

entity와 repository도 분리가 가능하지만, 2가지DB에 4가지 경로가 나오기때문에

여기서는 퉁쳐서 두가지 경로만 사용하겠습니다.


3. DataBaseConfigXX 클래스 생성

package com.example.demo.config;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
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.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;


primary db와 나머지 db들( 더 복수개로 설정하고 싶을때) 약간의 차이가 있으니

db를 더 추가하고 싶으면 second 설정내용을 복사하여 db추가를 하면되겠습니다.

하나의 Class에 다중 DB설정화도 가능하지만, 여기서는 Db별로 각각 분리를 하겠습니다.


각각의 DB설정롤을 정의하는 Class

  • DataBaseConfigPrimary
  • DataBaseConfigSecond


Primary DB설정

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
  basePackages = {"com.example.demo.data"})
public class DataBaseConfigPrimary {

	  @Primary
	  @Bean(name = "dataSource")
	  @ConfigurationProperties(prefix = "app.datasource.primary")
	  public DataSource dataSource() {
	    return DataSourceBuilder.create().build();
	  }

	  @Primary
	  @Bean(name = "entityManagerFactory")	  
	  public LocalContainerEntityManagerFactoryBean entityManagerFactory(
			  EntityManagerFactoryBuilder builder, 
			  @Qualifier("dataSource") DataSource dataSource,
			  Environment env) {
		Map<String, Object> properties = new HashMap<String, Object>();		
		properties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty("primary.hibernate.hbm2ddl.auto"));
		properties.put("hibernate.dialect", env.getRequiredProperty("primary.hibernate.dialect"));

	    return builder
	      .dataSource(dataSource)
	      .packages("com.example.demo.data")
	      .persistenceUnit("primary")
	      .properties(properties)
	      .build();
	  }

	  @Primary
	  @Bean(name = "transactionManager")
	  public PlatformTransactionManager transactionManager(
	    @Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
	    return new JpaTransactionManager(entityManagerFactory);
	  }

}

중요 설정 : 

  • com.example.demo.data : 패키지경로 설정

  • prefix = "app.datasource.primary" : application.conf에서 사용되는 prefix

  • "hibernate.hbm2ddl.auto", "create"


Second DB설정

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
  entityManagerFactoryRef = "secondEntityManagerFactory",
  transactionManagerRef = "secondTransactionManager",
  basePackages = {"com.example.demo.data2"})
public class DataBaseConfigSecond {
	
	  @Bean(name = "secondDataSource")
	  @ConfigurationProperties(prefix = "app.datasource.second")
	  public DataSource secondDataSource() {
	    return DataSourceBuilder.create().build();
	  }

	  @Bean(name = "secondEntityManagerFactory")
	  public LocalContainerEntityManagerFactoryBean secondEntityManagerFactory(
			  EntityManagerFactoryBuilder builder, 
			  @Qualifier("secondDataSource") DataSource secondDataSource,
			  Environment env) {
		Map<String, Object> properties = new HashMap<String, Object>();		
		properties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty("second.hibernate.hbm2ddl.auto"));
		properties.put("hibernate.dialect", env.getRequiredProperty("second.hibernate.dialect"));
		
		
	    return builder
	      .dataSource(secondDataSource)
	      .packages("com.example.demo.data2")
	      .persistenceUnit("second")
	      .properties(properties)
	      .build();
	  }

	  @Bean(name = "secondTransactionManager")
	  public PlatformTransactionManager secondTransactionManager(
	    @Qualifier("secondEntityManagerFactory") EntityManagerFactory secondEntityManagerFactory) {
	    return new JpaTransactionManager(secondEntityManagerFactory);
	  }
	
}

중요 설정 : 

  • com.example.demo.data2 : 패키지경로 설정

  • prefix = "app.datasource.second" : application.conf에서 사용되는 prefix

  • "hibernate.hbm2ddl.auto", "create" : ddl설정

  • secondEntityManagerFactory / secondTransactionManager / secondDataSource : db 별로이름충돌없이 맞추기



아래와같은 초기화 시도가 성공하고,에러없이 테이블을 자동으로 잘 만들어낸다고 하면

성공입니다.

Initialized JPA EntityManagerFactory for persistence unit 'primary'
Initialized JPA EntityManagerFactory for persistence unit 'second'



실제 작동되는 코드는 아래 샘플 코드에서 확인이 가능합니다.

http://git.webnori.com/projects/WEBF/repos/spring_jpa/browse











  • No labels