zoukankan      html  css  js  c++  java
  • mybatis多数据库切换,(动态数据源)。

    项目中将一个库的某些标的某些数据保存到另一个库。

    使用spring的aop编程动态切换数据源,代码如下,以备下次用到!

    1.先将两个数据库连接,创建两个数据源,交于spring管理!

    <bean id="dataSource"
            class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driver}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.user}" />
            <property name="password" value="${jdbc.password}" />
    </bean>
    
    <bean id="dataSource2"
            class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driver}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.user}" />
            <property name="password" value="${jdbc.password}" />
    </bean>

    2.将动态切换数据源的类交给spring

        <bean id="dynamicDataSource" class="cn.**.**.common.db.DynamicDataSource">
            <property name="targetDataSources">
                <map key-type="java.lang.String">
                    <!-- 指定lookupKey和与之对应的数据源 -->
                    <entry key="dataSource" value-ref="dataSource"></entry>
                    <entry key="dataSource2" value-ref="dataSource2"></entry>
                </map>
            </property>
            <!-- 这里可以指定默认的数据源 -->
            <property name="defaultTargetDataSource" ref="dataSource" />
        </bean>

    3.创建数据源切换的类,必须继承 AbstractRoutingDataSource 这个类(数据源动态切换必须继承该类)

    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        @Override
        protected Object determineCurrentLookupKey() {
            // 从自定义的位置获取数据源标识
            return DynamicDataSourceHolder.getDataSource();
        }
    
    }

    4.将数据源放进线程中,避免相互干扰

    public class DynamicDataSourceHolder {
        
        /**
         * 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰
         */
         private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();
         
         
         public static String getDataSource() {
             String dataSource = THREAD_DATA_SOURCE.get();
             if(StringUtils.isEmpty(dataSource)){
                 return DataSourceKey.DATA_SOURCE;
             }else{
                 return dataSource;
             }
         }
         
         public static void setDataSource(String dataSource) {
             THREAD_DATA_SOURCE.set(dataSource);
         }
         
         public static void clearDataSource() {
             THREAD_DATA_SOURCE.remove();
         }
    
    }

    5.数据源的key

    DataSourceKey 这个类是多个数据源在spring管理中的name标识,可通过key取得数据源。

    6.设置默认的数据源。

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DataSource {
        
        /**
         * 默认是某个数据源
         */
        String value() default DataSourceKey.DATA_SOURCE;
    
    }

    7.根据spring的aop在执行之前将数据源放进线程中,动态切换数据源。

    @Aspect
    @Component
    @Order(0)
    public class DataSourceAspect{
        
        private Logger logger = Logger.getLogger(getClass());
    
        @Pointcut("execution(public * cn.**.**.service..*Service.*(..))")
        public void dataSourceAspect(){};
        
        
        /**
         * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
         */
        @Before("dataSourceAspect()")
        public void before(JoinPoint point) throws Exception{
            Class<?> target = point.getTarget().getClass();
            MethodSignature signature = (MethodSignature) point.getSignature();
            // 默认使用目标类型的注解,如果没有则使用其实现接口的注解
            for (Class<?> clazz : target.getInterfaces()) {
                resolveDataSource(clazz, signature.getMethod());
            }
            resolveDataSource(target, signature.getMethod());
        }
        
    
        
        private void resolveDataSource(Class<?> clazz, Method method) {
            try {
                Class<?>[] types = method.getParameterTypes();
                // 先使用类型注解
                if (clazz.isAnnotationPresent(DataSource.class)) {
                    DataSource source = clazz.getAnnotation(DataSource.class);
                    DynamicDataSourceHolder.setDataSource(source.value());
                }else{
                    DynamicDataSourceHolder.clearDataSource();
                }
                // 方法注解可以覆盖类型注解
                Method m = clazz.getMethod(method.getName(), types);
                if (m != null && m.isAnnotationPresent(DataSource.class)) {
                    DataSource source = m.getAnnotation(DataSource.class);
                    DynamicDataSourceHolder.setDataSource(source.value());
                }
                logger.info("选择连接加载的DB :"+DynamicDataSourceHolder.getDataSource());
            } catch (Exception e) {
                //如果出现异常不能断了所有连接,默认还是某个数据库连接
                logger.error("选择连接加载DB ERROR "+clazz+":" +e.getMessage());
            }
        }
    }

    8.service层动态切换

    默认的数据源不用标明,另一个数据源用@DataSource(DataSourceKey.LYD_DATA_SOURCE) 在service上标明即可。

    9.另一个库的查询也可用@Results注解封装返回的实体(或map),sql用@Select注解

    10.在多数据源切换中,不要在一个事务方法里面用到两个数据源,不然会切换不过去。因为事务一般定义在service层,所以如果用到两个数据源,可以将两个数据源放在controller进行。

  • 相关阅读:
    python数据分析实例(1)
    Latex学习(1): longtable的分页显示保留表头与标题L
    pl/sql学习(5): 触发器trigger/事务和锁
    pl/sql学习(6): 引号/程序调试/列中的字符串合并/正则表达式
    pl/sql学习(4): 包package
    SQL入门(4): 嵌入式SQL语言
    我觉得vue原理就是 Object.defineProperty,简单实现了set和get,谢谢
    vue组件一
    css3 flex盒子布局
    php连接数据库、创建数据库、创建数据表
  • 原文地址:https://www.cnblogs.com/xiaoheis/p/8334944.html
Copyright © 2011-2022 走看看