Spring 配置多数据源并实现Druid自动切换

一、多数据源切换
   实现数据库的读写分离,这种情况往往是读多写少的情况,例如电商平台。既然数据库读写分离了,那么代码层也就需要读写不同的数据库了。实现方法应该有不少,我知道有插件实现,判断写请求还是读请求来请求不同的数据库,还有代码实现,不同的SQL访问不同的数据源,也就是接下来要说的多数据源。
  1、 类或者方法上只需要添加注解,即可实现多数据源切换,具体逻辑实现已经封装在数据访问层,业务逻辑处理层等多个组件进行使用
  2、类或者方法上添加如下注解:@DataSource(ContextConstant.DataSourceType.CTS) 或者@DataSource(ContextConstant.DataSourceType.REPORT) ,分别表示切换到XXX数据库和XXS数据库
   3、若需要再增加其他数据源,只需要在配置文件applicationContext.xml中增加数据配置信息,在常量接口ContextConstant中增量对应数据源类型即可。代码侵入性小,方便切换
二、代码配置信息
  1、applicationContext.xml配置


    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
       
    bean>

    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        <property name="connectionProperties" value="config.decrypt=true;config.decrypt.key=${cts.jdbc.publicKey}" />
    bean>


    
    <bean id="multipleDataSource" class="com.fqfin.credit.dbconfig.MultipleDataSource">
        
        <property name="defaultTargetDataSource" ref="reportDataSource" />
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="REPORT" value-ref="reportDataSource"/>
                <entry key="CTS" value-ref="ctsDataSource"/>
            map>
        property>
    bean>


    
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="multipleDataSource" />
      
    bean>


    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
       
        
        <property name="dataSource" ref="multipleDataSource"/>
        <property name="configLocation" value="classpath:spring/mybatis-config.xml"/>
        <property name="typeAliasesPackage" value="com.fqfin.**.dto" />
        
        <property name="mapperLocations" value="classpath:mapper/**/*Mapper.xml"/>
    bean>

  2、数据源实例

public interface ContextConstant {
     
      /**
    *@Description: 数据源枚举
    *@Param:  * @Param: null
    */
    enum DataSourceType {
     
        REPORT, CTS
    }
}
@Target({
     ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
     
    /**
    *@Description: 默认为上报数据库数据源
    */
    ContextConstant.DataSourceType value() default ContextConstant.DataSourceType.REPORT;
}

  2、切点表达式,biz数据访问层,business包下可以自动识别(须符合此种路径,方可生效,也可以自定义修改)

/**
 * @ClassName: DynamicDataSourceAspect
 * @Description: 切换数据源类
 */
@Component
@Aspect
@Order(1)   /**设置为1,优先加载,优先级高于AbstractRoutingDataSource的determineCurrentLookupKey()
              */
public class DynamicDataSourceAspect  {
     

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

    /**
    *@Description: 前置切面
    *@Param:  * @Param: null
 
    */
    @Before("execution(* com.fqfin.credit.business..*.*(..))")
    public void before(JoinPoint point) {
     
        try {
     
            DataSource annotationOfClass = point.getTarget().getClass().getAnnotation(DataSource.class);
            String methodName = point.getSignature().getName();
            Class[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
            Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);
            DataSource methodAnnotation = method.getAnnotation(DataSource.class);
            methodAnnotation = methodAnnotation == null ? annotationOfClass : methodAnnotation;
            ContextConstant.DataSourceType dataSourceType = methodAnnotation != null
                    && methodAnnotation.value() != null ? methodAnnotation.value() : ContextConstant.DataSourceType.REPORT;

            MultipleDataSourceHandler.setRouteKey(dataSourceType.name());
        } catch (NoSuchMethodException e) {
     
            log.error("aspect error,err={}",e);
        }
    }

    /**
    *@Description: 后置切面
    */
    @After("execution(* com.fqfin.credit.business..*.*(..))")
    public void after(JoinPoint point) {
     
        MultipleDataSourceHandler.removeRouteKey();
    }
}
/**
 * @ClassName: MultipleDataSource
 * @Description: 数据源路由实现类
 */
public class MultipleDataSource   extends AbstractRoutingDataSource {
     
    private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
    @Override
    protected Object determineCurrentLookupKey() {
     
        String dataSourceCofig = MultipleDataSourceHandler.getRouteKey();
        if (dataSourceCofig == null) {
     
            log.info("当前数据源为[report]");
        } else {
     
            log.info("当前数据源为{}", dataSourceCofig);
        }
        return dataSourceCofig;
    }
}

  3、使用ThreadLocal存储当前使用数据源实例的key

/**
 * @ClassName: MultipleDataSourceHandler
 * @Description: 多数据源持有类,数据源路由
 */
public class MultipleDataSourceHandler {
     
    private static final Logger logger = LoggerFactory.getLogger(MultipleDataSourceHandler.class);
    /**
     * 线程副本,保证线程安全
     */
    private static ThreadLocal<String> routeKey = new ThreadLocal<String>();

     /**
     *@Description: 绑定当前线程数据源路由的key 使用完成后必须调用removeRouteKey()方法删除
     */
    public static void setRouteKey(String key){
     
        logger.warn("系统切换到{}数据源",key);
        routeKey.set(key);
    }

    /**
    *@Description: 获取当前线程的路由
    */
    public static String getRouteKey(){
     
        return routeKey.get();
    }



    /**
    *@Description: 删除与当前线程绑定的路由key
    */
    public static void removeRouteKey(){
     
        routeKey.remove();
    }

}

  4、类或者方法上添加注解,实现路由

@DataSource(ContextConstant.DataSourceType.CTS)
@Service
public class BaiHangAccountService  extends AbstractService {
     
    @Autowired
    private BaihangLoanAccountInfoMapper baihangLoanAccountInfoMapper;

    public List<BaihangLoanAccountInfo> getList(BaihangLoanAccountInfoExample example, PageRequest pageRequest){
     
      
        return pageList;

    }
    @DataSource(ContextConstant.DataSourceType.REPORT)
    public void updateByPrimaryKeySelective( BaihangLoanAccountInfo baihangLoanAccountInfo){
     
        baihangLoanAccountInfoMapper.updateByPrimaryKeySelective(baihangLoanAccountInfo);
    }

}

你可能感兴趣的:(Spring,mysql,java)