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进行。

posted @ 2018-01-23 11:43  小黑s  阅读(1571)  评论(0编辑  收藏  举报