당근개발자
당근당근
당근개발자
전체 방문자
오늘
어제
  • 분류 전체보기 (157)
    • codepresoo (13)
    • JAVA (1)
    • JS (12)
    • 코딩 테스트 (7)
    • Spring (21)
    • 자료구조 (4)
    • HTTP (7)
    • JSP (36)
    • Etc (21)
    • RESTful (11)
    • React (1)
    • k8s (0)

블로그 메뉴

  • 홈

공지사항

인기 글

태그

  • 코드프레소. #codepresso #대외활동 # IT교육 #코딩강의 #코딩 이러닝 # 무료 IT교육 #무료 코딩교육
  • HATEAOD
  • queue#자료구조
  • dobule_linked_list#java
  • thymleaf#layout#fragment#thymleaf layout 적용안됌
  • S
  • Primitive#Wrapper
  • thymleaf error#template might not exist
  • @JsonFilter#당근개발자
  • #코드프레소. #codepresso #대외활동 # IT교육 #코딩강의 #코딩 이러닝 # 무료 IT교육 #무료 코딩교육
  • stack#자료구조
  • 한 번에 끝내는 코딩테스트 369 Java편 초격차 패키지 Online.
  • 한 번에 끝내는 코딩테스트 369 Java편 초격차 패키지 Online
  • 패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기 #수강하고있는강의명
  • 패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기
  • 이

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
당근개발자

당근당근

Spring

Spring DataSource 두개 사용하기(Abstract)

2022. 10. 13. 23:19

한번의 request 요청시, spring에서 두 개의 db에서 각각 값을 조회해야하는 경우가 생겼다.

datasource routing이 제대로 설정안돼, 오래동안 삽질한 결과를 적도로 하겠다.

 

보통은 한 개의 datasource를 사용할 경우, application.properties(yml) 에 db정보를 입력해주면, 자동으로 spring이 datasource정보를 등록을 해준다.

하지만, 멀티 datasource를 사용해야 할 경우, 수동으로 등록을 해주어야한다.

 

/resources/application.properties

// application.properties
datasource.main.driver-class-name=com.mysql.cj.jdbc.Driver
datasource.main.url=jdbc:mysql://xxx.xx.xx.xx:3306/schema?useLegacyDatetimeCode=false&serverTimezone=UTC
datasource.main.username=root
datasource.main.password=1234

datasource.front.driver-class-name=com.mysql.cj.jdbc.Driver
datasource.front.url=jdbc:mysql://xxx.xx.xx.xx:3306/schema?useLegacyDatetimeCode=false&serverTimezone=UTC
datasource.front.username=root
datasource.front.password=1234

필자는  datasource 다음 main과 front로 구분해서 지정하였다.

 

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

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
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.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;

import com.zaxxer.hikari.HikariDataSource;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
// datasource 설정을 적용할 상위 패키지
	basePackages = "com.partridge.tools.dhst",
	entityManagerFactoryRef = "entityManager",
	transactionManagerRef = "transactionManager"
)
public class DatabaseConfig {
    
    @Bean(name="datasourceConfig")
	@Primary
	public DataSource dataSource() {
		DataSourceRouting dataSourceRouting = new DataSourceRouting();
		dataSourceRouting.setTargetDataSources(targetDataSources());
		dataSourceRouting.setDefaultTargetDataSource(mainDataSource());
		return dataSourceRouting;
	}

	private Map<Object, Object> targetDataSources() {
		Map<Object, Object> targetDataSources = new HashMap<>();
		targetDataSources.put(DatabaseEnum.FRONT, frontDataSource());
		targetDataSources.put(DatabaseEnum.MAIN, mainDataSource());
		return targetDataSources;
	}

	@Bean
	@ConfigurationProperties("datasource.main") // application.properties에 있는 정보로, datasource 기본 정보 생성
	public DataSourceProperties mainDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	public DataSource mainDataSource() {
		return mainDataSourceProperties()
			.initializeDataSourceBuilder()
			.type(HikariDataSource.class)
			.build();
	}

	@Bean
	@ConfigurationProperties("datasource.front") 
	public DataSourceProperties frontDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	public DataSource frontDataSource() {
		return frontDataSourceProperties()
			.initializeDataSourceBuilder()
			.type(HikariDataSource.class)
			.build();
	}

	@Bean(name = "entityManager")
	public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(
		EntityManagerFactoryBuilder builder) {
			
		return builder.dataSource(dataSource()).packages("com.partridge.tools.dhst")
			.build();
	}
// datasource 관리할 트랜잭션 매니저 등록
	@Bean(name = "transactionManager")
	public PlatformTransactionManager  transactionManager(
		@Autowired @Qualifier("entityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
		JpaTransactionManager tm = new JpaTransactionManager(entityManagerFactoryBean.getObject());
		tm.setDataSource(dataSource());
		return tm;
	}
}

datasource 등록을 위한 databaseconfig 자바를 작성해준다.

 

 

그리고 multi db를 routing을 위한 로직을 작성한다.

/RoutingDatabaseContextHolder

import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

@Component
public class RoutingDatabaseContextHolder {

    private static ThreadLocal<DatabaseEnum> CONTEXT= new ThreadLocal<>();
    
    public static void set(DatabaseEnum clientDatabase) {

        Assert.notNull(clientDatabase, "RoutingDatabase cannot be null");
        CONTEXT.set(clientDatabase);
    }


    public static DatabaseEnum getClientDatabase() {

        System.out.println("context : "+CONTEXT.get());
       if(CONTEXT.get()==null|| CONTEXT.get() ==DatabaseEnum.MAIN){
        return DatabaseEnum.MAIN;
       }
       return DatabaseEnum.FRONT;

    }


    public static void clear() {

        CONTEXT.remove();
    }
    
}
public enum DatabaseEnum {
    MAIN,FRONT;

}
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DataSourceRouting extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {

        return RoutingDatabaseContextHolder.getClientDatabase();

    }
    
}
 @GetMapping("/pagelog")
    public ResponseEntity<?> retrievePageLong(Pageable pageable) throws SQLException{

        RoutingDatabaseContextHolder.set(DatabaseEnum.MAIN);
        System.out.println("main db method CALL BEFORE");
        Result<UserResponseDto> retrieveUserByProject = adminService.retrieveUserByProject(4, 4, pageable);
        System.out.println("main :" +retrieveUserByProject.getCount());
        

        RoutingDatabaseContextHolder.set(DatabaseEnum.FRONT);
      

       platformTransactionManager.getTransaction(new DefaultTransactionDefinition());
       
        System.out.println("FRONT db method CALL BEFORE");
            
        List<PageLog> retrievePageLog = pageLogService.retrievePageLog(pageable);
        RoutingDatabaseContextHolder.clear();

        return ResponseEntity.ok().body(retrievePageLog);
    }

'Spring' 카테고리의 다른 글

BindingResult  (0) 2022.01.23
어노테이션 만들기(@Qualifier)  (0) 2021.11.12
조회 빈이 2개이상일 경우(@Qualifier,@Primary)  (0) 2021.11.12
롬북  (0) 2021.11.10
생성자 주입 선택  (0) 2021.11.10
    'Spring' 카테고리의 다른 글
    • BindingResult
    • 어노테이션 만들기(@Qualifier)
    • 조회 빈이 2개이상일 경우(@Qualifier,@Primary)
    • 롬북
    당근개발자
    당근개발자

    티스토리툴바