关于spring+springMvc+mybatis整合,配置多数据源

spring整合mybatis配置文件部分

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 加载数据库配置文件 -->
<context:property-placeholder location="classpath:database/jdbc.properties"
ignore-unresolvable="true" />

<!-- 开启AOP监听 只对当前配置文件有效 -->
<aop:aspectj-autoproxy expose-proxy="true" />

<!-- 配置源数据库数据源-->
<bean id="dataSourceSqlServer" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="driverClassName" value="${enbrands.driverClassName}" />
<property name="url" value="${enbrands.url}" />
<property name="username" value="${enbrands.username}" />
<property name="password" value="${enbrands.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${druid.initialSize}" />
<property name="minIdle" value="${druid.minIdle}" />
<property name="maxActive" value="${druid.maxActive}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${druid.maxWait}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${druid.validationQuery}" />
<property name="testWhileIdle" value="${druid.testWhileIdle}" />
<property name="testOnBorrow" value="${druid.testOnBorrow}" />
<property name="testOnReturn" value="${druid.testOnReturn}" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。 -->
<property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" />
<property name="maxPoolPreparedStatementPerConnectionSize"
value="${druid.maxPoolPreparedStatementPerConnectionSize}" />
<!-- 配置监控统计拦截的filters -->
<property name="filters" value="${druid.filters}" />
</bean>
<!-- 配置目标数据库数据源-->
<bean id="dataSourceMySql" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="driverClassName" value="${target.driverClassName}" />
<property name="url" value="${target.url}" />
<property name="username" value="${target.username}" />
<property name="password" value="${target.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${druid.initialSize}" />
<property name="minIdle" value="${druid.minIdle}" />
<property name="maxActive" value="${druid.maxActive}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${druid.maxWait}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${druid.validationQuery}" />
<property name="testWhileIdle" value="${druid.testWhileIdle}" />
<property name="testOnBorrow" value="${druid.testOnBorrow}" />
<property name="testOnReturn" value="${druid.testOnReturn}" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。 -->
<property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" />
<property name="maxPoolPreparedStatementPerConnectionSize"
value="${druid.maxPoolPreparedStatementPerConnectionSize}" />
<!-- 配置监控统计拦截的filters -->
<property name="filters" value="${druid.filters}" />
</bean>

<bean id="dataSourceProxy"
class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="dataSource" />
</bean>

<!--指定多数据源  -->
  <bean id="dataSource" class="com.jjmc.dcl.core.DynamicDataSource">  
      <property name="targetDataSources">  
          <map key-type="java.lang.String">
              <!-- 指定lookupKey和与之对应的数据源 -->
              <entry key="dataSourceSqlServer" value-ref="dataSourceSqlServer"></entry>  
              <entry key="dataSourceMySql" value-ref="dataSourceMySql"></entry>  
          </map>  
      </property>  
      <!-- 这里可以指定默认的数据源 -->
      <property name="defaultTargetDataSource" ref="dataSourceSqlServer" />  
</bean> 

<!--事务管理器配置 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceProxy" />
</bean>

<!-- 声明SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mapping/*.xml" />
<!-- <property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
                    <value>
                        helperDialect=oracle
                        reasonable=true
                        supportMethodsArguments=true
                        params=count=countSql
                        autoRuntimeDialect=true
                    </value>
                </property>
</bean>
</array>
</property> -->
</bean>

<!-- 扫描QMS系统数据层接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.jjmc.dcl.dao" />
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
<tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
<tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
</tx:attributes>
</tx:advice>

  <bean id="dataSourceAspect" class="com.jjmc.dcl.core.DynamicDataSourceInterceptor" />
     <aop:config>
         <aop:aspect ref="dataSourceAspect">
             <!-- 拦截所有service方法 -->
             <aop:pointcut id="dataSourcePointcut" expression="execution(* com.jjmc.dcl.service.impl.*.*(..))"/>
             <aop:before pointcut-ref="dataSourcePointcut" method="intercept" />
         </aop:aspect>
     </aop:config>

<!-- <aop:config expose-proxy="true" proxy-target-class="true">
只对业务逻辑层实施事务
<aop:pointcut id="txPointcut"
expression="execution(* com.jjmc.dcl.service..*.*(..))" />
<aop:advisor id="txAdvisor" advice-ref="txAdvice"
pointcut-ref="txPointcut" />
</aop:config> -->

</beans>

<!--------------------------------------------------------------------------------------------------------------------------------------------------->

除了配置spring,还需要另外再加三个类用来切换数据源

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 *
 * @author huangtao
 * 2017年10月16日下午12:08:10
 * business-server
 * @parameter
 * TODO
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
// 从自定义的位置获取数据源标识
return DynamicDataSourceHolder.getDataSource();
}
}

<!--------------------------------------------------------------------------------------------------------------------------------------------------->

/**
 *
 * @author huangtao
 * 2017年10月16日下午12:09:08
 * business-server
 * @parameter
 * TODO
 *
 */
public class DynamicDataSourceHolder {
    /**
    * 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰
    */
    private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();
    public static String getDataSource() {
         return THREAD_DATA_SOURCE.get();
    }
    public static void setDataSource(String dataSource) {
        THREAD_DATA_SOURCE.set(dataSource);
    }
    public static void clearDataSource() {
        THREAD_DATA_SOURCE.remove();
    }
}

<!--------------------------------------------------------------------------------------------------------------------------------------------------->

import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import com.jjmc.dcl.service.DataSource;

/**
 *
 * @author huangtao
 * 2017年10月16日下午12:20:49
 * business-server
 * @parameter
 * TODO
 *
 */
public class DynamicDataSourceInterceptor {
//  public void setdataSourceMysql(JoinPoint jp)
//  {
//  DynamicDataSourceHolder.setDataSource("dataSourceSqlServer");
//  }
//
//  public void setdataSourceSql(JoinPoint jp) {
//  DynamicDataSourceHolder.setDataSource("dataSourceMySql");
//  }
//  
   public void intercept(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());
    }
 
    /**
     * 提取目标对象方法注解和类型注解中的数据源标识
     * 
     * @param clazz
     * @param method
     */
    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());
            }
            // 方法注解可以覆盖类型注解
            Method m = clazz.getMethod(method.getName(), types);
            if (m != null && m.isAnnotationPresent(DataSource.class)) {
                DataSource source = m.getAnnotation(DataSource.class);
                DynamicDataSourceHolder.setDataSource(source.value());
            }
        } catch (Exception e) {
            System.out.println(clazz + ":" + e.getMessage());
        }
    }
}

注意:第三个类中注释掉的代码是手动切换,没注释的代码是注解识别,注解可以定义在serviceImpl和dao的方法上,这个项目是定义在serviceImpl的方法上。

如果要手动切换数据源,那么需要DynamicDataSourceHolder.setDataSource("dataSourceSqlServer");写在java代码中,进行切换数据源。

最后,再定义一个注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
/**
 *
 * @author huangtao
 * 2017年10月16日下午12:57:23
 * business-server
 * @parameter
 * TODO
 * 自定义注解DataSource
 */
@Target(value = { ElementType.TYPE,ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface DataSource {
String value();
}

<!--------------------------------------------------------------------------------------------------------------------------------------------------->

这样就完成了注解自动切换数据源

























posted @ 2017-10-29 20:52  me-ht  阅读(153)  评论(0编辑  收藏  举报