사용이유
datasource 커넥션을 로직이나 상황별로 나눠야 하는 상황이 올 수 도 있고, 어플리케이션단에서 샤딩을 구현해야 할 수 도 있고, read 커넥션 및 write 커넥션의 datasource 를 나눠야 하는 경우가 있다.
애초에 다른 datasoure 파일을 만들어도 되지만, 동적으로 쿼리 요청시마다 datasource 를 변경해야 할 때 AbstractRoutingDataSource 를 사용하면 된다.
AbstractRoutingDataSource란?
spring-jdbc 모듈에 포함되어 있는 클래스로, 여러 DataSource를 등록하고 특정 상황에 맞게 원하는 DataSource를 사용할 수 있도록 추상화한 클래스이다.
구현방식
AbstractRoutingDataSource는 이름 기반으로 다중 DataSource 정보를 등록하는 방법이다.
AbstractRoutingDataSource의
public void setTargetDataSources(Map<object, object=""> targetDataSource)를 호출하여
String:DataSource을 키:값으로하는 Datasource를 Map에 저장할 수 있다.
AbstractRoutingDataSource 의 determineCurrentLooupKey()를 오버라이드해서 상황에 맞게 Key를 반환하도록 구현해야 한다.
어떤 데이터 소스를 쓸지에 대한 결정은 determineCurrentLooupKey 메소드 내부에서 쿼리 호출시마다 동적으로 결정된다.
return 되는 String 결과값에 해당되는 datasource 를 실행하게 된다.
public class MyRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
if (isReadOnly) {
return "slave";
} else {
return "master";
}
}
}
참고로 boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); 를 사용하려면
transactionManager 에 LazyConnectionDataSourceProxy 를 설정해 주어야 한다.
@Bean("RoutingDataSource")
public DataSource routingDataSource() {
HikariDataSource masterDataSource = createDatasource(~~);
HikariDataSource slaveDataSource = createDatasource(~~);
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("master", masterDataSource);
dataSourceMap.put("slave", slaveDataSource);
DynamicRoutingDataSource routingDataSource = new MyRoutingDataSource();
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(masterDataSource);
return routingDataSource;
}
@Bean("routingLazyDataSource")
public DataSource routingLazyDataSource(@Qualifier("RoutingDataSource") DataSource dataSource) {
return new LazyConnectionDataSourceProxy(dataSource);
}
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("routingLazyDataSource") DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
public HikariDataSource createDatasource() {
// 데이터 connection 정보
~~
return new HikariDataSource(config);
}
LazyConnectionDataSourceProxy 는 데이터소스를 한번 더 감싸는 역할을 해주어, LazyConnectionDataSourceProxy 로 여러 데이터 소스를 묶어 PlatformTransactionManager 에 datasource 를 세팅해야, tranaction 이 정확히 동작한다.
다음과 같이 PlatformTransactionManager 에 LazyConnectionDataSourceProxy 로 datasource 여러개를 감싼 datasource 를 set 해줘야
TransactionSynchronizationManager.isCurrentTransactionReadOnly() 를 할 경우 정확히 readOnly 를 사용하는 쿼리를 캐치해서 분리 할 수 있다.
@Transactional(readOnly = true)
public List<Test> findTestList() {
return testMapper.selectTestList();
}
다음과 같이 readOnly 가 true 인 경우는 isCurrentTransactionReadOnly 가 true 이므로 determineCurrentLookupKey 메소드에서 "slave" datasource 를 반환하게 된다.
만약에 위에서 만든 여러 datasource 를 LazyConnectionDataSourceProxy 로 감싸지 않으면 readOnly 가 true 인지를 알 수 가 없다.
위처럼도 사용가능하고, determineCurrentLookupKey 를 어떻게 구현하느냐에 따라 db 정보를 다르게 사용할 수 있다.
'Spring > spring boot 및 기타' 카테고리의 다른 글
[spring boot] 스프링 부트 2.1 변경 내역 (0) | 2021.08.31 |
---|---|
[spring] Mybatis batch upsert 하는 방법(mysql) (0) | 2021.08.11 |
[spring boot] mybatis 연동 및 설정 (0) | 2021.06.23 |
[spring boot] 스프링 firebase database 사용하기 (1) | 2021.05.04 |
[spring boot] 스프링 부트 h2 인메모리 db 시작하기 (0) | 2021.04.27 |