zoukankan      html  css  js  c++  java
  • spring+mybatis多数据源,动态切换

    有时我们项目中需要配置多个数据源,不同的业务使用的数据库不同


    实现思路:
    配置多个dataSource ,再配置多个sqlSessionFactory,和dataSource一一对应。
    重写SqlSessionTemplate,支持多个sqlSessionFactory。最后配置mybatis自动扫描MapperScannerConfigurer。增加切面,在访问数据库前动态获取数据源

    相关配置

        <!-- 配置sqlSessionFactory -->
        <bean id="sqlSessionFactory_product" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource_product" />
            <property name="configLocation" value="classpath:mybatis-config.xml" />
            <property name="mapperLocations" value="classpath*:com/zyx/demo/**/*Mapper.xml"/>
        </bean>
        <!-- 配置sqlSessionFactory -->
        <bean id="sqlSessionFactory_promotion" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource_promotion" />
            <property name="configLocation" value="classpath:mybatis-config.xml" />
            <property name="mapperLocations" value="classpath*:com/zyx/demo/**/*Mapper.xml"/>
        </bean>
    
    
        <!-- 重写sqlSessionTemplate实现,支持多个SQLSessionFactory -->
        <bean id="dynamicSqlSessionTemplate" class="com.zyx.demo.common.mybatis.DynamicSqlSessionTemplate">
            <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory_product" />
            <property name="targetSqlSessionFactorys">
                <map key-type="java.lang.String">
                    <entry value-ref="sqlSessionFactory_product" key="PRODUCT" />
                    <entry value-ref="sqlSessionFactory_promotion" key="PROMOTION" />
                </map>
            </property>
            <property name="defaultTargetSqlSessionFactory" ref="sqlSessionFactory_product" />
        </bean>
    
        <!-- 配置MapperScannerConfigurer -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionTemplateBeanName" value="dynamicSqlSessionTemplate" />
            <property name="basePackage" value="com.zyx.demo.**.dao" />
        </bean>
    
        <!-- 定义切面 -->
        <bean id="dynamicDataSourceAspectJ" class="com.zyx.demo.common.aop.DynamicDataSourceAspectJ" />
        <aop:config proxy-target-class="false" expose-proxy="true">
            <aop:pointcut expression="execution(* com.zyx.demo.*.dao..*.*(..))" id="datasource-pointcut" />
            <aop:aspect ref="dynamicDataSourceAspectJ">
                <aop:before method="switchDataSource" pointcut-ref="datasource-pointcut" />
            </aop:aspect>
        </aop:config>

    重写sqlSessionTemplate

    public class DynamicSqlSessionTemplate extends SqlSessionTemplate {
    
        private static Logger logger = LoggerFactory.getLogger(DynamicSqlSessionTemplate.class);
    
        private SqlSessionFactory sqlSessionFactory;
        private ExecutorType executorType;
        private SqlSession sqlSessionProxy;
        private PersistenceExceptionTranslator exceptionTranslator;
        private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;
        private SqlSessionFactory defaultTargetSqlSessionFactory;
    
        public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {
            this.targetSqlSessionFactorys = targetSqlSessionFactorys;
        }
    
        public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
            this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
        }
    
        public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
            this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
        }
    
        public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
            this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
        }
    
        public DynamicSqlSessionTemplate(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());
            this.defaultTargetSqlSessionFactory = sqlSessionFactory;
        }
    
        @Override
        public SqlSessionFactory getSqlSessionFactory() {
            String dataSourceType = DynamicDataSourceContextHolder.getDataSourceType();
            SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(dataSourceType);
            if (targetSqlSessionFactory != null) {
                return targetSqlSessionFactory;
            } else if (defaultTargetSqlSessionFactory != null) {
                if (dataSourceType != null) {
                    logger.warn("此[" + dataSourceType + "]dataSourceType未配置文件中配置targetSqlSessionFactorys,将会返回defaultTargetSqlSessionFactory来执行后面的操作");
                }
                return defaultTargetSqlSessionFactory;
            } else {
                Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
                Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
            }
            return this.sqlSessionFactory;
        }
    
        @Override
        public Configuration getConfiguration() {
            return this.getSqlSessionFactory().getConfiguration();
        }
    
        public ExecutorType getExecutorType() {
            return this.executorType;
        }
    
        public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
            return this.exceptionTranslator;
        }
    
        public <T> T selectOne(String statement) {
            return this.sqlSessionProxy.<T> selectOne(statement);
        }
    
        public <T> T selectOne(String statement, Object parameter) {
            return this.sqlSessionProxy.<T> selectOne(statement, parameter);
        }
    
        public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
            return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
        }
    
        public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
            return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
        }
    
        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);
        }
    
        public <E> List<E> selectList(String statement) {
            return this.sqlSessionProxy.<E> selectList(statement);
        }
    public <E> List<E> selectList(String statement, Object parameter) { return this.sqlSessionProxy.<E> selectList(statement, parameter); } public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds); } public void select(String statement, ResultHandler handler) { this.sqlSessionProxy.select(statement, handler); } public void select(String statement, Object parameter, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, handler); } public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, rowBounds, handler); } public int insert(String statement) { return this.sqlSessionProxy.insert(statement); } public int insert(String statement, Object parameter) { return this.sqlSessionProxy.insert(statement, parameter); } public int update(String statement) { return this.sqlSessionProxy.update(statement); } public int update(String statement, Object parameter) { return this.sqlSessionProxy.update(statement, parameter); } public int delete(String statement) { return this.sqlSessionProxy.delete(statement); } public int delete(String statement, Object parameter) { return this.sqlSessionProxy.delete(statement, parameter); } public <T> T getMapper(Class<T> type) { return getConfiguration().getMapper(type, this); } public void commit() { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } public void commit(boolean force) { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } public void rollback() { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } public void rollback(boolean force) { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } public void close() { throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession"); } public void clearCache() { this.sqlSessionProxy.clearCache(); } public Connection getConnection() { return this.sqlSessionProxy.getConnection(); } public List<BatchResult> flushStatements() { return this.sqlSessionProxy.flushStatements(); } private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = getSqlSession(DynamicSqlSessionTemplate.this.getSqlSessionFactory(), DynamicSqlSessionTemplate.this.executorType, DynamicSqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory())) { sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (DynamicSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory()); sqlSession = null; Throwable translated = DynamicSqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory()); } } } } }

    增加切面

    public class DynamicDataSourceAspectJ {
    
        private Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspectJ.class);
    
        /**
         * 切换数据源*/
        public void switchDataSource(JoinPoint joinPoint) {
            try {
                Signature signature = joinPoint.getSignature();
                MethodSignature methodSignature = (MethodSignature) signature;
                Method method = methodSignature.getMethod();
                boolean methodAnnotation = method.isAnnotationPresent(DynamicDataSource.class);
                DynamicDataSource dynamicDataSource = method.getAnnotation(DynamicDataSource.class);
                if (!methodAnnotation) {
                    Class<?> clazzs[] = joinPoint.getTarget().getClass().getInterfaces();
                    if (clazzs != null && clazzs.length>0) {
                        Class<?> clazz = clazzs[0];
                        dynamicDataSource = (DynamicDataSource) clazz.getAnnotation(DynamicDataSource.class);
                        if (dynamicDataSource != null) {
                            DynamicDataSourceContextHolder.setDataSourceType(dynamicDataSource.dataSourceType());
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("切换数据源异常:" + e.getMessage(), e);
            }
        }
    }
    public class DynamicDataSourceContextHolder {
    
        /**
         * DataSource上下文,每个线程对应相应的数据源key
         */
        public static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
        private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
        public static void setDataSourceType(String dataSourceType) {
            contextHolder.set(dataSourceType);
        }
    
        public static String getDataSourceType() {String dataSourceType = contextHolder.get();
            logger.debug("当前线程Thread:" + Thread.currentThread().getName() + " 当前的数据源 key is " + dataSourceType);
            return dataSourceType;
        }
    
        public static void clearDataSourceType() {
            contextHolder.remove();
        }
    }

    自定义标签,需要在dao层的接口上声明数据源

    @Target({ ElementType.METHOD, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface DynamicDataSource {
    
        public final static String PRODUCT = "PRODUCT";
        public final static String PROMOTION = "PROMOTION";
    
        String dataSourceType() default DynamicDataSource.PRODUCT;
    }
  • 相关阅读:
    洛谷-P1149 火柴棒等式
    洛谷-P1036 选题
    【linux】CPU,内存对网站的影响
    【jenkins】【java】【tomcat】windows host key verification failed
    【java】A local class access to local variables
    【java】类成员的访问限制关系
    【android】6大布局
    【android】android对位图文件的支持
    【android】签署应用采用相同证书的用处
    【markdown】图片的处理
  • 原文地址:https://www.cnblogs.com/zhangyaxiao/p/8342867.html
Copyright © 2011-2022 走看看