Spring 3 + Mybatis 3 통합 시 다 중 데이터 원본 동적 전환 문제

이전 프로젝트 경력 에 서 는 기본적으로 Spring + Hibernate + Spring JDBC 라 는 조합 이 많이 사용 됐다.마 이 바 티 스 는 이 프로젝트 에 대해 서 만 시험 적 으로 사용 하기 시 작 했 고 잡담 을 많이 하지 않 고 본론 으로 들 어 갔다.
예전 의 이런 프레임 워 크 조합 에서 동적 데이터 소스 전환 은 이미 매우 성숙 했다 고 할 수 있다. 인터넷 에 도 매우 많은 블 로그 소개 가 있 는데 모두 AbstractRouting DataSource 를 계승 하여 determineCurrentLookupkey () 방법 을 다시 쓰 는 것 이다.구체 적 인 방법 은 여기 서 쓸데없는 말 을 하지 않 겠 다.
그래서 프로젝트 에서 이 문제 에 부 딪 혔 을 때 나 는 거의 생각 지도 못 하고 이런 방법 을 채택 했다. 그러나 테스트 를 하 자 아무런 반응 이 없 었 다.그 때 는 불가능 하 다 고 생각 하여 정지점 에 log 디 버 깅 을 했 는데 determineCurrentLookupkey () 가 호출 되 지 않 았 습 니 다. 
왜 열거 합 니까?그 럴 리 가 없 는데.마음 을 가 라 앉 히 고 곰 곰 이 생각해 보 니 중요 한 문제 가 생각 났 습 니 다. Mybatis 통합 Spring, Spring 통합 Mybatis 가 아 닙 니 다!직감 적 으로 나 에 게 문 제 는 바로 여기에 있다.
그래서 시간 을 들 여 my batis - spring. jar 라 는 가방 을 연구 한 결과 SqlSession 이라는 것 이 있 는 것 을 발견 하고 본능적으로 이 부분 을 알 아차 린 다음 에 그의 실현 류 를 대충 살 펴 보 았 다.그래서 그의 실현 류 안에 내부 류 Sql Session Interceptor 가 있다 는 것 을 발견 했다.
좋 습 니 다. 이런 역할 열 은 바로 session Proxy 를 만 드 는 것 입 니 다.핵심 코드 는 다음 과 같다.
      final SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);

이 sqlSession Factory 는 우리 가 잘 알 고 있 습 니 다. spring 프로필 에 배 치 된 것 입 니 다. 그 렇 죠? 즉, 이 물건 은 우리 프로필 에서 직접 읽 어 들 인 것 입 니 다. 그러나 이 물건 은 Datasource 와 연결 되 어 있 습 니 다.그래서 만약 에 이 물건 을 동태 적 으로 할 수 있다 면 데이터 소스 가 전환 되면 동태 적 인 것 이 라 고 생각 했다.
그래서 첫 번 째 반응 은 하나의 종 류 를 쓴 다음 에 그 안에 하나의 맵 을 정의 하여 여러 개의 SqlSession Factory 를 저장 하고 Setter 방법 으로 속성 주입 을 하 는 것 이다.
public class EjsSqlSessionTemplate extends SqlSessionTemplate {

    private Map<String, SqlSessionFactory> targetSqlSessionFactory = new HashMap<String, SqlSessionFactory>();
    public void setTargetSqlSessionFactory(Map<String, SqlSessionFactory> targetSqlSessionFactory) {
        this.targetSqlSessionFactory = targetSqlSessionFactory;
    }

그래서 Spring 의 프로필 은 이렇게 되 었 습 니 다.
 <bean id="sqlSessionTemplate" class="com.ejushang.spider.datasource.EjsSqlSessionTemplate">
        <constructor-arg ref="sqlSessionFactory" />
        <property name="targetSqlSessionFactory">
            <map>
                <entry value-ref="sqlSessionFactory" key="spider"/>
                <entry value-ref="sqlSessionFactoryTb" key="sysinfo"/>
            </map>
        </property>
    </bean>

    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.foo.bar.**.mapper*" />
        <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
    </bean>

그렇다면 이 사상 은 어디에서 온 열 입 니까?물론 Spring 의 동적 데이터 소스 사상 을 참고 한 것 입 니 다. Spring 동적 데이터 소스 의 설정 을 비교 해 보면 차이 가 많 지 않 습 니까?
그리고 중요 한 방법 을 다시 썼 습 니 다.
/**
     *     SqlSessionFactory   
     * @return
     */
    @Override
    public SqlSessionFactory getSqlSessionFactory() {

        SqlSessionFactory targetSqlSessionFactory = this.targetSqlSessionFactory.get(SqlSessionContextHolder.getDataSourceKey());
        if (targetSqlSessionFactory != null) {
            return targetSqlSessionFactory;
        } else if ( this.getSqlSessionFactory() != null) {
            return  this.getSqlSessionFactory();
        }
        throw new IllegalArgumentException("sqlSessionFactory or targetSqlSessionFactory must set one at least");
    }

그리고 SqlSession ContextHolder 는 간단 하 다. 바로 ThreadLocal 의 사상 이다.
public class SqlSessionContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    private static Logger logger = LoggerFactory.getLogger(SqlSessionContextHolder.class);

    public static void setSessionFactoryKey(String dataSourceKey) {
        contextHolder.set(dataSourceKey);
    }

    public static String getDataSourceKey() {
        String key = contextHolder.get();
        logger.info("    Thread:"+Thread.currentThread().getName()+"        key is "+ key);
        return key;
    }

}

블 로 거들 은 자신 감 이 넘 쳐 테스트 를 시작 했다.그 결과 안 되 고 전환 이 안 되 는 것 을 발 견 했 습 니 다. 구조 함수 에 있 는 기본 적 인 sqlSession Factory 가 연결 되 어 있 었 습 니 다. 그 때 는 하루 종일 소스 코드 를 보 았 기 때문에 머리 도 어 지 러 웠 습 니 다.사실 왜 열 거 했 어 요?
우리 가 session Proxy 를 만 드 는 곳 코드 를 보 세 요. 그의 sqlSession Factory 는 구조 함수 에서 직접 가 져 왔 습 니 다.구조 함수 에 있 는 sqlSession Factory 는 spring 용기 가 시 작 될 때 초기 화 되 었 습 니 다. 이 점 은 우리 Spring 프로필 에서 도 확인 할 수 있 습 니 다.
그럼 이 문 제 는 열 을 어떻게 해결 합 니까?그래서 블 로 거들 은 그 sqlSession Interceptor 를 다시 쓰 려 고 했다.닦 아, 문제 가 생 겼 어. 이 종 류 는 private 거 야. 다시 쓸 수가 없 잖 아.그래서 블 로 거들 은 자신의 EjsSqlSessionTemplate 류 에서 만 내부 류 를 정의 하고 소스 코드 의 코드 를 모두 복사 합 니 다. 유일한 차이 점 은 제 가 구조 함수 중의 sqlSessionFactory 를 읽 는 것 이 아니 라 매번 getSqlSessionFactory () 방법 을 호출 하 는 것 입 니 다.코드 는 다음 과 같 습 니 다:
 final SqlSession sqlSession = getSqlSession(
                    EjsSqlSessionTemplate.this.getSqlSessionFactory(),
                    EjsSqlSessionTemplate.this.getExecutorType(),
                    EjsSqlSessionTemplate.this.getPersistenceExceptionTranslator());

다시 시도 해 보 니 안 되 었 다. 다시 원인 을 찾 아 아까 그 문제 로 돌 아 왔 다.SqlSessionTemplate 의 구조 함 수 를 다시 쓰 지 않 았 기 때문에 sqlSessionProxy 는 구조 함수 에서 초기 화 되 었 습 니 다. 코드 는 다음 과 같 습 니 다.
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

SqlSession Interceptor () 는 모두 private 입 니 다.그래서 부 류 는 내 가 쓴 그 SqlSession Interceptor () 를 전혀 불 러 오지 않 을 것 이다.그래서 문 제 는 바로 여기에 있다. 그래, 블 로 거들 은 또 구성 함 수 를 다시 쓴다.
  public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        super(getSqlSessionFactory(), executorType, exceptionTranslator);
    }

이 코드 는 컴 파일 이 통과 되 지 않 는 것 이 분명 합 니 다. 구조 함수 에서 어떻게 클래스 인 스 턴 스 방법 열 을 호출 할 수 있 습 니까? 그럼 어떻게 열?또 부계 의 구조 함수 copy 를 가 져 올 수 밖 에 없 었 다. 그 문 제 는 또 생 겼 고 이 구성원 들 의 속성 도 없 었 다.그럼 어 쩔 수 없 이 그들 도 옮 겨 와 야 지. 나중에 이 동적 데이터 소스 의 기능 이 마침내 완성 되 었 다. 
전체 완전한 코드 는 다음 과 같다.
1. SqlSessionTemplate 재 작성 (재 작성 과정 은 이미 위 에서 분석)
public class EjsSqlSessionTemplate extends SqlSessionTemplate {

    private final SqlSessionFactory sqlSessionFactory;
    private final ExecutorType executorType;
    private final SqlSession sqlSessionProxy;
    private final PersistenceExceptionTranslator exceptionTranslator;

    private Map<Object, SqlSessionFactory> targetSqlSessionFactory;

    public void setTargetSqlSessionFactory(Map<Object, SqlSessionFactory> targetSqlSessionFactory) {
        this.targetSqlSessionFactory = targetSqlSessionFactory;
    }


    public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
    }

    public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
        this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()
                .getEnvironment().getDataSource(), true));
    }

    public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
                                    PersistenceExceptionTranslator exceptionTranslator) {

        super(sqlSessionFactory, executorType, exceptionTranslator);

        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;

        this.sqlSessionProxy = (SqlSession) newProxyInstance(
                SqlSessionFactory.class.getClassLoader(),
                new Class[] { SqlSession.class },
                new SqlSessionInterceptor());

    }



    @Override
    public SqlSessionFactory getSqlSessionFactory() {

        SqlSessionFactory targetSqlSessionFactory = this.targetSqlSessionFactory.get(SqlSessionContextHolder.getDataSourceKey());
        if (targetSqlSessionFactory != null) {
            return targetSqlSessionFactory;
        } else if ( this.sqlSessionFactory != null) {
            return  this.sqlSessionFactory;
        }
       throw new IllegalArgumentException("sqlSessionFactory or targetSqlSessionFactory must set one at least");
    }

    @Override
    public Configuration getConfiguration() {
        return this.getSqlSessionFactory().getConfiguration();
    }

    public ExecutorType getExecutorType() {
        return this.executorType;
    }

    public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
        return this.exceptionTranslator;
    }

    /**
     * {@inheritDoc}
     */
    public <T> T selectOne(String statement) {
        return this.sqlSessionProxy.<T> selectOne(statement);
    }

    /**
     * {@inheritDoc}
     */
    public <T> T selectOne(String statement, Object parameter) {
        return this.sqlSessionProxy.<T> selectOne(statement, parameter);
    }

    /**
     * {@inheritDoc}
     */
    public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
        return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
    }

    /**
     * {@inheritDoc}
     */
    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
        return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
    }

    /**
     * {@inheritDoc}
     */
    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
        return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);
    }

    /**
     * {@inheritDoc}
     */
    public <E> List<E> selectList(String statement) {
        return this.sqlSessionProxy.<E> selectList(statement);
    }

    /**
     * {@inheritDoc}
     */
    public <E> List<E> selectList(String statement, Object parameter) {
        return this.sqlSessionProxy.<E> selectList(statement, parameter);
    }

    /**
     * {@inheritDoc}
     */
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
    }

    /**
     * {@inheritDoc}
     */
    public void select(String statement, ResultHandler handler) {
        this.sqlSessionProxy.select(statement, handler);
    }

    /**
     * {@inheritDoc}
     */
    public void select(String statement, Object parameter, ResultHandler handler) {
        this.sqlSessionProxy.select(statement, parameter, handler);
    }

    /**
     * {@inheritDoc}
     */
    public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
        this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
    }

    /**
     * {@inheritDoc}
     */
    public int insert(String statement) {
        return this.sqlSessionProxy.insert(statement);
    }

    /**
     * {@inheritDoc}
     */
    public int insert(String statement, Object parameter) {
        return this.sqlSessionProxy.insert(statement, parameter);
    }

    /**
     * {@inheritDoc}
     */
    public int update(String statement) {
        return this.sqlSessionProxy.update(statement);
    }

    /**
     * {@inheritDoc}
     */
    public int update(String statement, Object parameter) {
        return this.sqlSessionProxy.update(statement, parameter);
    }

    /**
     * {@inheritDoc}
     */
    public int delete(String statement) {
        return this.sqlSessionProxy.delete(statement);
    }

    /**
     * {@inheritDoc}
     */
    public int delete(String statement, Object parameter) {
        return this.sqlSessionProxy.delete(statement, parameter);
    }

    /**
     * {@inheritDoc}
     */
    public <T> T getMapper(Class<T> type) {
        return getConfiguration().getMapper(type, this);
    }

    /**
     * {@inheritDoc}
     */
    public void commit() {
        throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
    }

    /**
     * {@inheritDoc}
     */
    public void commit(boolean force) {
        throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
    }

    /**
     * {@inheritDoc}
     */
    public void rollback() {
        throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
    }

    /**
     * {@inheritDoc}
     */
    public void rollback(boolean force) {
        throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
    }

    /**
     * {@inheritDoc}
     */
    public void close() {
        throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
    }

    /**
     * {@inheritDoc}
     */
    public void clearCache() {
        this.sqlSessionProxy.clearCache();
    }

    /**
     * {@inheritDoc}
     */
    public Connection getConnection() {
        return this.sqlSessionProxy.getConnection();
    }

    /**
     * {@inheritDoc}
     * @since 1.0.2
     */
    public List<BatchResult> flushStatements() {
        return this.sqlSessionProxy.flushStatements();
    }

    /**
     * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
     * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to
     * the {@code PersistenceExceptionTranslator}.
     */
    private class SqlSessionInterceptor implements InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            final SqlSession sqlSession = getSqlSession(
                    EjsSqlSessionTemplate.this.getSqlSessionFactory(),
                    EjsSqlSessionTemplate.this.executorType,
                    EjsSqlSessionTemplate.this.exceptionTranslator);
            try {
                Object result = method.invoke(sqlSession, args);
                if (!isSqlSessionTransactional(sqlSession, EjsSqlSessionTemplate.this.getSqlSessionFactory())) {
                    // force commit even on non-dirty sessions because some databases require
                    // a commit/rollback before calling close()
                    sqlSession.commit(true);
                }
                return result;
            } catch (Throwable t) {
                Throwable unwrapped = unwrapThrowable(t);
                if (EjsSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    Throwable translated = EjsSqlSessionTemplate.this.exceptionTranslator
                            .translateExceptionIfPossible((PersistenceException) unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }
                throw unwrapped;
            } finally {
                closeSqlSession(sqlSession, EjsSqlSessionTemplate.this.getSqlSessionFactory());
            }
        }
    }
}

2。주석 을 사용자 정의 하 였 습 니 다.
/**
 *       ,         
 * User:Amos.zhou
 * Date: 14-2-27
 * Time:   2:34
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChooseDataSource {

    String value() default "";
}

3. AspectJ 의 절단면 을 정의 합 니 다. (저 는 AspectJ 를 사용 하 는 습관 이 있 습 니 다. spring AOP 는 cflow () 라 는 문법 을 지원 하지 않 기 때문에 컴 파일 할 때 aspectJ 의 컴 파일 러 를 사용 해 야 합 니 다. 원생 JDK 를 직접 사용 할 수 없습니다.어떤 방법 은 내 가 예전 에 Hibernate, JDBC 동적 데이터 원본 을 바탕 으로 바 꾼 것 이다.
/**
 * <li>   :        ,    ,        ,         </li>
 *
 * @author: amos.zhou
 * 2013-8-1   11:51:40
 * @since v1.0
 */
@Aspect
public abstract class ChooseDataSourceAspect {

    protected static final ThreadLocal<String> preDatasourceHolder = new ThreadLocal<String>();

    @Pointcut("execution(public * *.*(..))")
    public void allMethodPoint() {

    }

    @Pointcut("@within(com.ejushang.spider.annotation.ChooseDataSource) && allMethodPoint()")
    public void allServiceMethod() {

    }


    /**
     *       ChooseDataSource      
     */
    @Pointcut("cflow(allServiceMethod()) && allServiceMethod()")
    public void changeDatasourcePoint() {
    }


    /**
     *   @ChooseDataSource         dataSourceKey,  DynamicDataSource
     */
    @Before("changeDatasourcePoint()")
    public void changeDataSourceBeforeMethodExecution(JoinPoint jp) {
        //  anotation       
        String resultDS = determineDatasource(jp);
        //           
        if (resultDS == null) {
            SqlSessionContextHolder.setSessionFactoryKey(null);
            return;
        }
        preDatasourceHolder.set(SqlSessionContextHolder.getDataSourceKey());
        //             
        SqlSessionContextHolder.setSessionFactoryKey(resultDS);

    }

    /**
     * <p>    : 2013-8-20   9:48:44</p>
     *               ,      
     *
     * @param jp
     * @return
     */
    @SuppressWarnings("rawtypes")
    protected String determineDatasource(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        Class targetClass = jp.getSignature().getDeclaringType();
        String dataSourceForTargetClass = resolveDataSourceFromClass(targetClass);
        String dataSourceForTargetMethod = resolveDataSourceFromMethod(
                targetClass, methodName);
        String resultDS = determinateDataSource(dataSourceForTargetClass,
                dataSourceForTargetMethod);
        return resultDS;
    }


    /**
     *         ,            。
     *   foo()      bar(),  bar()       ,
     * bar()   ,        ,     ,    foo()       ,  
     * foo()    。
     * <p>    : 2013-8-16   4:27:06</p>
     */
    @After("changeDatasourcePoint()")
    public void restoreDataSourceAfterMethodExecution() {
        SqlSessionContextHolder.setSessionFactoryKey(preDatasourceHolder.get());
        preDatasourceHolder.remove();
    }


    /**
     * <li>    : 2013-6-17   5:34:13</li> <li>   :amos.zhou</li> <li>     :</li>
     *
     * @param targetClass
     * @param methodName
     * @return
     */
    @SuppressWarnings("rawtypes")
    private String resolveDataSourceFromMethod(Class targetClass,
                                               String methodName) {
        Method m = ReflectUtil.findUniqueMethod(targetClass, methodName);
        if (m != null) {
            ChooseDataSource choDs = m.getAnnotation(ChooseDataSource.class);
            return resolveDataSourcename(choDs);
        }
        return null;
    }

    /**
     * <li>    : 2013-6-17   5:06:02</li>
     * <li>   :amos.zhou</li>
     * <li>     :   
     *      ,           ,        ,         ,       ,        ,        </li>
     *
     * @param classDS
     * @param methodDS
     * @return
     */
    private String determinateDataSource(String classDS, String methodDS) {
//        if (null == classDS && null == methodDS) {
//            return null;
//        }
        //         null,      Null,    Null
        return methodDS == null ? classDS : methodDS;
    }

    /**
     * <li>    : 2013-6-17   4:33:03</li> <li>   :amos.zhou</li> <li>     :      @ChooseDataSource
     *    </li>
     *
     * @param targetClass
     * @return
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private String resolveDataSourceFromClass(Class targetClass) {
        ChooseDataSource classAnnotation = (ChooseDataSource) targetClass
                .getAnnotation(ChooseDataSource.class);
        //           
        return null != classAnnotation ? resolveDataSourcename(classAnnotation)
                : null;
    }

    /**
     * <li>    : 2013-6-17   4:31:42</li> <li>   :amos.zhou</li> <li>     :
     *   DataSource   </li>
     *
     * @param ds
     * @return
     */
    private String resolveDataSourcename(ChooseDataSource ds) {
        return ds == null ? null : ds.value();
    }

}

그러면 상기 세 가지 유형 은 하나의 공공 구성 요소 로 가방 을 만 들 수 있 습 니 다.
그러면 항목 중 구체 적 으로 열 을 어떻게 사용 합 니까?
4.  프로젝트 에서 구체 적 인 AspectJ 절단면 을 정의 합 니 다.
@Aspect
public class OrderFetchAspect extends ChooseDataSourceAspect {
}

만약 당신 의 필요 에 따라 다시 쓰 는 방법 이 있다 면, 내 쪽 은 다시 쓰 지 않 아 도 되 기 때문에 빈 절단면 이면 됩 니 다.
5. spring 을 설정 합 니 다. 위의 분석 과정 에서 이미 붙 여 졌 습 니 다. 기본적으로 모든 데이터 베이스, 하나의 dataSource, 모든 DataSource 의 SqlSession Factory 입 니 다.마지막 으로 Sql Session Template, 즉 우리 가 다시 쓴 것 입 니 다.그리고 MapperScan 입 니 다. 대체적으로 다음 과 같 습 니 다.
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        
    </bean>

    <bean id="dataSourceTb" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        
    </bean>

    <!--      -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!--        -->
    <tx:annotation-driven transaction-manager="transactionManager"/>


    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations" value="classpath*:com/foo/bar/**/config/*mapper.xml" />
    </bean>

    <bean id="sqlSessionFactoryTb" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSourceTb"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations" value="classpath*:com/foo/bar/**/configtb/*mapper.xml" />
    </bean>

    <bean id="sqlSessionTemplate" class="com.foo.bar.template.EjsSqlSessionTemplate">
        <constructor-arg ref="sqlSessionFactory" />
        <property name="targetSqlSessionFactory">
            <map>
                <entry value-ref="sqlSessionFactory" key="spider"/>
                <entry value-ref="sqlSessionFactoryTb" key="sysinfo"/>
            </map>
        </property>
    </bean>

    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.foo.bar.**.mapper*" />
        <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
    </bean>

6. 구체 적 인 응용
@ChooseDataSource("spider")
public class ShopServiceTest extends ErpTest {

    private static final Logger log = LoggerFactory.getLogger(ShopServiceTest.class);

    @Autowired
    private IShopService shopService;

    @Autowired
    private IJdpTbTradeService jdpTbTradeService;


    @Test
    @Rollback(false)
    public void testFindAllShop(){
        List<Shop> shopList1 = shopService.findAllShop();
        for(Shop shop : shopList1){
            System.out.println(shop);
        }

        fromTestDB();
    }

    @ChooseDataSource("sysinfo")
    private void fromTestDB(){
        List<Shop> shopList = jdpTbTradeService.findAllShop();
        for(Shop shop : shopList){
            System.out.println(shop);
        }
    }
}

테스트 결과 shop List 1 은 spider 라 이브 러 리 에서 나 온 데이터 이 고 from DB 는 sysinfo 에서 나 온 데이터 입 니 다.그럼 큰 성 과 를 거 두 겠 습 니 다. 
내 이상 의 기능 을 하려 면 Spring AOP 는 할 수 없다. 왜냐하면 그 는 Cflow () 를 지지 하지 않 기 때문이다. 이것 이 바로 내 가 왜 AspectJ 를 사용 해 야 하 는 이유 이다.
------------------------------------------------------------------------------------------------------------------------------
자, 기능 은 우리 가 이미 실현 되 었 습 니 다. 당신 은 매우 번 거 롭 게 생각 하지 않 습 니까? 이것 도 Spring 의 스타일 이 아 닙 니 다. Spring 의 각 지역 확장 은 매우 편리 합 니 다.그럼 Sql Session Template 의 어느 곳 을 바 꾸 면 우 리 는 이 기능 열 을 쉽게 실현 할 수 있 습 니까?여러분 은 이해 하 실 수 있 습 니 다. 다시 가서 소스 코드 를 보 세 요.
사실 원본 코드 에 있 는 SqlSession Interceptor 의 이 말 을:
 final SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);

다음으로 변경:
 final SqlSession sqlSession = getSqlSession(
                    EjsSqlSessionTemplate.this.getSqlSessionFactory(),
                    EjsSqlSessionTemplate.this.executorType,
                    EjsSqlSessionTemplate.this.exceptionTranslator);

세 션 에이전트 가 생 길 때마다 들 어 오 는 인 자 는 getSqlSession Factory () 를 호출 하여 가 져 옵 니 다. 그러면 사용자 정의 SqlSession Template 는 getSqlSession Factory () 를 다시 쓰기 만 하면 다음 과 같은 두 마디 를 추가 합 니 다.
 private Map<Object, SqlSessionFactory> targetSqlSessionFactory;

    public void setTargetSqlSessionFactory(Map<Object, SqlSessionFactory> targetSqlSessionFactory) {
        this.targetSqlSessionFactory = targetSqlSessionFactory;
    }

그러면 동적 데이터 원본 전환 을 충분히 실현 할 수 있다. 그럼 my batis - spering 프로젝트 팀 은 이렇게 유지 할 수 있 습 니까?나 는 메 일 로 그들 과 소통 할 것 이다.개선 할 수 있 을 지 없 을 지 에 대해 서 우 리 는 알 수 없다.
사실 이것 은 대상 을 대상 으로 디자인 할 때의 사상 을 불 러 일 으 키 는 동시에 끊임없이 논쟁 하 는 문제 이기 도 하 다.
    클래스 의 방법 에서 클래스 의 속성 을 사용 하려 면 this. filedName 을 직접 사용 합 니 다. 하 다 getFiledName () 을 사용 하 시 겠 습 니까?
사실 예전 에 저도 this. 속성 으로 직접 조작 하 는 편 이 었 지만 이번 일 을 겪 은 후에 저 는 후자 에 치 우 칠 것 이 라 고 생각 합 니 다.

좋은 웹페이지 즐겨찾기