Spring 의 AbstractRouting DataSource 를 사용 하여 다 중 데이터 원본 전환 예제 구현

최근 에 프로젝트 는 두 항목 간 의 데이터 동기 화 에 대한 수요 가 필요 하기 때문에 구체 적 으로 프로젝트 1 의 데 이 터 는 메시지 큐 를 통 해 프로젝트 2 에 동기 화 됩 니 다.이 업데이트 작업 은 여러 라 이브 러 리 의 데 이 터 를 업데이트 하 는 것 과 관련 되 기 때문에 다 중 데이터 소스 전환 작업 이 필요 합 니 다.스프링 에서 데이터 원본 전환 을 어떻게 하 는 지 설명 한다.여 기 는 AbstractRouting DataSource 류 를 사용 하여 구체 적 인 조작 을 완성 하고,AbstractRouting DataSource 는 Spring 2.0 이후 증가 합 니 다.

데이터 원본 전환 을 실현 하 는 기능 은 바로 확장 AbstractRouting DataSource 추상 류 를 사용자 정의 하 는 것 입 니 다.사실은 데이터 원본 DataSourcer 의 경로 중개 에 해당 하 며 프로젝트 가 실 행 될 때 해당 key 값 에 따라 해당 하 는 데이터 원본 DataSource 로 전환 할 수 있 습 니 다.먼저 AbstractRouting DataSource 의 소스 코드 를 보 세 요.

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
  /*         */
  private Map<Object, Object> targetDataSources;

  private Object defaultTargetDataSource;

  private boolean lenientFallback = true;

  private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();

  private Map<Object, DataSource> resolvedDataSources;

  private DataSource resolvedDefaultDataSource;

  @Override
  public Connection getConnection() throws SQLException {
    return determineTargetDataSource().getConnection();
  }

  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return determineTargetDataSource().getConnection(username, password);
  }

  protected DataSource determineTargetDataSource() {
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
    Object lookupKey = determineCurrentLookupKey();
    DataSource dataSource = this.resolvedDataSources.get(lookupKey);
    if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
      dataSource = this.resolvedDefaultDataSource;
    }
    if (dataSource == null) {
      throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
    }
    return dataSource;
  }

  protected abstract Object determineCurrentLookupKey();
}

소스 코드 를 통 해 알 수 있 듯 이 AbstractRouting DataSource 는 AbstractDataSource 를 계승 하고 InitialingBean,AbstractRouting DataSource 의 getConnection()방법 은 determineTargetDataSource()의 이 방법 을 호출 했다.여기 서 determineTargetDataSource()방법 코드 에 중점 을 두 고 determineCurrentLookupkey()방법 을 사용 했다.이것 은 AbstractRouting DataSource 류 의 추상 적 인 방법 이자 데이터 원본 전환 을 실현 하 는 확장 방법 입 니 다.이 방법의 반환 값 은 프로젝트 에 사용 할 DataSource 의 key 값 입 니 다.이 key 를 가 져 오 면 resolvedDataSource 에서 해당 하 는 DataSource 를 꺼 낼 수 있 습 니 다.key 가 해당 하 는 DataSource 를 찾 지 못 하면 기본 데이터 원본 을 사용 합 니 다.
사용자 정의 클래스 확장 AbstractRouting DataSource 클래스 는 determineCurrentLookupkey()방법 을 다시 써 서 데이터 원본 전환 기능 을 실현 합 니 다.다음은 사용자 정의 확장 AbstractRouting DataSource 클래스 의 실현 입 니 다.

/**
 *      
 */
public class MultipleDataSource extends AbstractRoutingDataSource{

  @Override
  protected Object determineCurrentLookupKey() {
     return DynamicDataSourceHolder.getRouteKey();
  }
}
DynamicDataSourceHolder 클래스 는 다음 과 같 습 니 다.데이터 원본 에 대한 조작 기능 을 실현 합 니 다.

/**
 *       
 */
public class DynamicDataSourceHolder {
  private static ThreadLocal<String> routeKey = new ThreadLocal<String>();

  /**
   *              key
   */
  public static String getRouteKey()
  {
    String key = routeKey.get();
    return key;
  }

  /**
   *             key
   *          removeRouteKey()    
   */
  public static void setRouteKey(String key)
  {
    routeKey.set(key);
  }

  /**
   *                 key
   */
  public static void removeRouteKey()
  {
    routeKey.remove();
  }
}

다음은 xml 파일 에 여러 개의 데이터 원본 을 설정 합 니 다:

<!--     -->
<bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver">
   </property>
   <property name="url" value="jdbc:jtds:sqlserver://127.0.0.1;databaseName=test">
   </property>
   <property name="username" value="***"></property>
   <property name="password" value="***"></property>
 </bean>
 <bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver">
   </property>
   <property name="url" value="jdbc:jtds:sqlserver://127.0.0.2:1433;databaseName=test">
   </property>
   <property name="username" value="***"></property>
   <property name="password" value="***"></property>
</bean>

<!--          -->
<bean id="multipleDataSource" class="MultipleDataSource" >
   <property name="targetDataSources">
     <map key-type="java.lang.String">
       <entry value-ref="dataSource1" key="dataSource1"></entry>
       <entry value-ref="dataSource2" key="dataSource2"></entry>
     </map>
   </property>
   <!--       -->
   <property name="defaultTargetDataSource" ref="dataSource1" >
   </property>
</bean>
여기까지 기본 적 인 설정 이 완성 되 었 습 니 다.다음은 데이터 원본 을 전환 해 야 하 는 곳 에서 호출 방법 을 사용 하면 됩 니 다.보통 dao 층 에서 데이터 베 이 스 를 조작 하기 전에 전환 합 니 다.데이터 베 이 스 를 조작 하기 전에 다음 과 같은 코드 를 추가 하면 됩 니 다.

DynamicDataSourceHolder.setRouteKey("dataSource2");
위 에서 소개 한 것 은 dao 층 에서 데이터 원본 을 전환 해 야 할 때 수 동 으로 데이터 원본 을 전환 하 는 코드 를 추가 하거나 AOP 방식 을 사용 할 수 있 습 니 다.설 정 된 데이터 원본 형식 을 모두 주석 라벨 로 설정 하고 dao 층 에서 데이터 원본 을 전환 하 는 방법 이나 클래스 에 주석 라벨 을 써 서 조작 성 이 더욱 강 합 니 다.

@DataSourceKey("dataSource1")
public interface TestEntityMapper extends MSSQLMapper<TestEntity> {
  public void insertTest(TestEntity testEntity);
}
DataSourceKey 주석 코드 는 다음 과 같 습 니 다.

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceKey {
  String value() default "";
}
주석 설정 이 끝 난 후에 데이터 원본 전환 을 실현 하 는 클래스 를 써 야 합 니 다.다음 과 같 습 니 다.

public class MultipleDataSourceExchange {

  /** 
   *       ,   @DataSource        ,                
   */ 
  public void beforeDaoMethod(JoinPoint point) throws Exception { 
    Class<?> target = point.getTarget().getClass(); 
    MethodSignature signature = (MethodSignature) point.getSignature(); 
    //            ,                 
    for (Class<?> cls : target.getInterfaces()) { 
      resetDataSource(cls, signature.getMethod()); 
    } 
    resetDataSource(target, signature.getMethod()); 
  } 


  /** 
   *                       
   */ 
  private void resetDataSource(Class<?> cls, Method method) { 
    try { 
      Class<?>[] types = method.getParameterTypes(); 
      //         
      if (cls.isAnnotationPresent(DataSourceKey.class)) { 
        DataSourceKey source = cls.getAnnotation(DataSourceKey.class); 
        DynamicDataSourceHolder.setRouteKey(source.value()); 
      } 
      //             
      Method m = cls.getMethod(method.getName(), types); 
      if (m != null && m.isAnnotationPresent(DataSourceKey.class)) { 
        DataSourceKey source = m.getAnnotation(DataSourceKey.class);  
        DynamicDataSourceHolder.setRouteKey(source.value()); 
      } 
    } catch (Exception e) { 
      System.out.println(cls + ":" + e.getMessage()); 
    } 
  } 
}

코드 를 다 쓴 후 xml 프로필 에 설정 을 추가 해 야 합 니 다.(부분 설정 만 표시)

<bean id="multipleDataSourceExchange" class="MultipleDataSourceExchange "/>

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="multipleDataSource" />
</bean>

<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
    <tx:method name="insert*" propagation="NESTED" rollback-for="Exception"/>
    <tx:method name="add*" propagation="NESTED" rollback-for="Exception"/>
    ...
  </tx:attributes>
</tx:advice>

<aop:config>
  <aop:pointcut id="service" expression="execution(* com.datasource..*.service.*.*(..))"/>
  <!--                     -->
  <aop:advisor advice-ref="multipleDataSourceExchange" pointcut-ref="service" order="1"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="service" order="2"/>
</aop:config>
AOP 방식 으로 다 중 데이터 원본 의 동적 전환 이 완료 되 었 습 니 다.

좋은 웹페이지 즐겨찾기