본문 바로가기
  • 실행력이 모든걸 결정한다
Spring Series/Spring Framework

[Spring] DelegatingDataSource

by 김코더 김주역 2022. 10. 23.
반응형

1. DelegatingDataSource란?

- DataSource와 DataSource 타입의 프로퍼티를 갖는 bean 사이에서 부가기능을 제공해주는 bean을 만들 때 쓰이는 기반 클래스다.

- targetDataSource 프로퍼티에 실제 DataSource bean을 넣어 위임만 해준다. 그리고 자신은 DataSource인 것처럼 위장해서 트랜잭션 매니저나 DAO에 DI될 수 있다.

- DelegatingDataSource 기반의 클래스로는 대표적으로 LazyConnectionDataSourceProxy와 AbstractRoutingDataSource가 있다.

 

 

2. LazyConnectionDataSourceProxy

- DB 커넥션(자원)을 최대한 늦게 생성해주는 프록시 패턴이다. 트랜잭션이 시작하고 최초로 SQL을 실행하는 시점 사이에 많은 작업들이 있다면 DB 커넥션을 미리 가져온 것은 자원의 낭비라고 볼 수 있으며, 이를 해결하기 위한 방법이다.

- 트랜잭션이 시작되면 실제 DB 커넥션 풀로부터 Connection을 가져오는 대신 가상의 프록시 Connection을 가져온다. 그리고 최초로 SQL이 실행되기 전까지 트랜잭션 설정 정보를 LazyConnectionDataSourceProxy 내에 보관해두고 DB 커넥션이 필요한 시점에서 실제 DataSource에 적용해준다.

- 트랜잭션 AOP의 모든 장점과 DB 커넥션의 사용을 최적화할 수 있게 된다.

 

Bean 등록

<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
    <property name="targetDataSource" ref="realDataSource"/>
</bean>

 

- JPA같은 ORM을 사용하는 경우에는 2차 캐시와 함께 사용하면 더 효과적이다. 2차 캐시는 애플리케이션 범위의 캐시로 불필요한 DB 호출을 줄일 수 있다. 2차 캐시에 대해 더 알고 싶다면 아래 포스팅을 참고하자.

https://kimcoder.tistory.com/510

 

[JPA] 2차 캐시

1. 캐시의 사용 이유 - 네크워크(외부 DB) 호출은 애플리케이션 서버의 내부 메모리에 접근하는 것보다 시간 비용이 훨씬 비싸기 때문에 캐시를 이용하는 것이 성능에 좋다. 2. 1차 캐시와 2차 캐시

kimcoder.tistory.com

 

 

 

3. AbstractRoutingDataSource

- 다중 DataSource에 대한 라우팅을 제공하는 프록시다.

- 추상 클래스이기 때문에 상속을 통해 기능을 추가해서 사용하면 된다.

- lookup 키와 DataSource 맵을 정의해야 한다. lookup 키는 AbstractRoutingDataSource의 서브클래스에서 determineCurrentLookupKey() 메소드에서 리턴하도록 하면 된다.

public class ReadOnlyRoutingDataSource extends AbstractRoutingDataSource {
    protected Object determineCurrentLookupKey() {
        return TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "READONLY" : "READWRITE;
    }
}

그리고 이 서브클래스를 bean으로 등록하고 각 lookup 키에 대한 DataSource를 정의해주면 된다.

<bean id="dataSourceRouter" class="com.example.demo.datasource.router.ReadOnlyRoutingDataSource">
    <property name="targetDataSources">
        <map>
            <entry key="READONLY" value-ref="readOnlyDataSource"/>
            <entry key="READWRITE" value-ref="readWriteDataSource"/>
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="readWriteDataSource"/>
</bean>

- Spring은 AbstractRoutingDataSource의 서브클래스로 IsolationLevelDataSourceRouter를 제공하는데, IsolationLevelDataSourceRouter은 트랜잭션 격리 수준에 따라 다른 DataSource를 사용할 수 있도록 한다.

※ IsolationLevelDataSourceRouter 문서 : https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/datasource/lookup/IsolationLevelDataSourceRouter.html

※ JTA는 일부 WAS를 제외하면 트랜잭션 단위로 격리수준을 설정할 수 없다. 이 때는 격리수준이 다르게 설정된 JNDI DataSource를 등록해서 사용하면 된다.

 

 

 

● 참고 자료 : 토비의 스프링 3.1 Vol.2

 

반응형

댓글