美文网首页
SSM动态配置多数据源

SSM动态配置多数据源

作者: Clark_ | 来源:发表于2020-03-18 21:05 被阅读0次

    1. 数据源配置 spring.xml

    <!-- 配置动态数据源开始 -->
        <!-- 配置连接池 druid 数据源,用于访问mysql数据库(${}这种写法称为占位符,具体值在运行时使用db.properties文件中配置的值) com.alibaba.druid.pool.DruidDataSource-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <property name="url" value="${jdbc.url}${jdbc.dbname1}?${jdbc.params}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
            <property name="initialSize" value="${initialSize}"/>
            <property name="maxActive" value="${maxActive}"/>
            <property name="minIdle" value="${minIdle}"/>
            <property name="maxIdle" value="${maxIdle}"/>
            <property name="maxWait" value="${maxWait}"/>
            <property name="validationQuery" value="${validationQuery}"/>
            <property name="poolPreparedStatements" value="${poolPreparedStatements}"/>
            <property name="maxPoolPreparedStatementPerConnectionSize" value="${maxPoolPreparedStatementPerConnectionSize}"/>
            <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}"/>
            <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}"/>
            <!-- 配置监控统计拦截的filters -->
            <property name="filters" value="stat"/>
        </bean>
    
        <bean id="dataSourceE" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <property name="url" value="${jdbc.url}${jdbc.dbname2}?${jdbc.params}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
            <property name="initialSize" value="${initialSize}"/>
            <property name="maxActive" value="${maxActive}"/>
            <property name="minIdle" value="${minIdle}"/>
            <property name="maxIdle" value="${maxIdle}"/>
            <property name="maxWait" value="${maxWait}"/>
            <property name="validationQuery" value="${validationQuery}"/>
            <property name="poolPreparedStatements" value="${poolPreparedStatements}"/>
            <property name="maxPoolPreparedStatementPerConnectionSize" value="${maxPoolPreparedStatementPerConnectionSize}"/>
            <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}"/>
            <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}"/>
            <!-- 配置监控统计拦截的filters -->
            <property name="filters" value="stat"/>
        </bean>
    
        <!-- 自定义数据源切换类 -->
        <bean id="myDataSource" class="com.zqu.DataSource.DynamicRoutingDataSource">
            <!-- 这里可以指定默认的数据源 -->
            <property name="defaultTargetDataSource" ref="dataSource" />
            <property name="targetDataSources">
                <map key-type="java.lang.String">
                    <!-- 指定lookupKey和与之对应的数据源 -->
                    <entry key="dataSource" value-ref="dataSource"></entry>
                    <entry key="dataSourceE" value-ref="dataSourceE"></entry>
                </map>
            </property>
        </bean>
        <!-- 配置动态数据源结束 -->
    

    2、配置数据源动态切面 spring.xml

    <!-- 数据源动态切换切面配置 -->
        <aop:config>
            <aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor" order="1">
                <!-- 拦截所有service实现类的方法 -->
                <aop:pointcut id="dataSourcePointcut"
                              expression="execution(* com.zqu.service..*Impl.*(..))"/>
                <aop:before pointcut-ref="dataSourcePointcut" method="intercept" />
            </aop:aspect>
        </aop:config>
    
        <!-- 数据源动态切换实体 -->
        <bean id="dataSourceInterceptor" class="com.zqu.DataSource.DynamicDataSourceInterceptor"/>
        <!-- 数据源动态切换切面配置结束 -->
    

    3、配置数据库连接参数 jdbc.properties

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://127.0.0.1:3306/
    jdbc.dbname1=family
    jdbc.dbname2=employees
    jdbc.params= useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    jdbc.username=root
    jdbc.password=123
    #配置初始化大小、最小、最大
    initialSize=0
    maxActive=8
    minIdle=0
    maxIdle=8
    
    #配置获取连接等待超时的时间
    maxWait=20000
    validationQuery=select 1
    
    #打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements=true
    maxPoolPreparedStatementPerConnectionSize=10
    
    #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis=60000
    
    #配置一个连接在池中最小生存的时间,单位是毫秒
    
    minEvictableIdleTimeMillis=1800000
    

    4、定义一个类继承DynamicRoutingDataSource实现determineCurrentLookupKey方法,来实现数据库的动态切换

    
    import org.apache.log4j.Logger;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    
    public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
        private static final Logger LOG=Logger.getLogger(DynamicRoutingDataSource.class);
        @Override
        protected Object determineCurrentLookupKey() {
            LOG.info("当前数据源:{}"+DynamicDataSourceContextHolder.get());
            return  DynamicDataSourceContextHolder.get();
        }
    }
    

    5、定义工具类DynamicDataSourceContextHolder ,用于动态切换数据源

    import org.apache.log4j.Logger;
    
    /**
     * @ClassName DynamicDataSourceContextHolder
     * @Description: TODO
     * @Author Jason
     * @Date 2020/3/18  12:15
     * @Version V1.0
     **/
    public class DynamicDataSourceContextHolder {
        private  static  final Logger LOG=Logger.getLogger(DynamicDataSourceContextHolder.class);
        private static final ThreadLocal<String> currenDataSource=new ThreadLocal<>();
        /**
         * @Author Jason
         * @Description //清除当前数据源
         * @Date 2020/3/18 12:19
         * @Param
         * @return
         **/
        public static  void clear(){
            currenDataSource.remove();
        }
       /**
        * @Author Jason
        * @Description //Description
        * @Date 2020/3/18 20:50
        * @Param []
        * @return java.lang.String
        **/
        public static String get(){
            return currenDataSource.get();
        }
    
        /**
         * @Author Jason
         * @Description 设置数据源
         * @Date 2020/3/18 15:49
         * @Param []
         * @return void
         **/
        public static void setDataSource(String datasource) {
            currenDataSource.set(datasource);
        }
    }
    
    

    6、自定义注解@TargetDataSource,通过注解的值来获取当前数据源,并进行切换

    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    /**
     * @ClassName TargetDataSource
     * @Description: 定义注解
     * @Author Jason
     * @Date 2020/3/18  17:07
     * @Version V1.0
     **/
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public  @interface TargetDataSource {
       String value();
    }
    

    7、定义拦截器DynamicDataSourceInterceptor,解析注解切换数据源

    import org.apache.log4j.Logger;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.reflect.MethodSignature;
    
    import java.lang.reflect.Method;
    
    
    /**
     * @ClassName DynamicDataSourceInterceptor
     * @Description: 数据源切换拦截器
     * @Author Jason
     * @Date 2020/3/18  17:32
     * @Version V1.0
     **/
    public class DynamicDataSourceInterceptor {
        private static final Logger LOG = Logger.getLogger(DynamicDataSourceInterceptor.class);
        /**
         * @Author Jason
         * @Description 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
         * @Date 2020/3/18 17:39
         * @Param [point]
         * @return void
         **/
        public void intercept(JoinPoint point) throws Exception {
            Class<?> target = point.getTarget().getClass();
            MethodSignature signature = (MethodSignature) point.getSignature();
            resolveDataSource(target, signature.getMethod());
        }
    
        /**
         * @Author Jason
         * @Description 提取目标对象方法注解和类型注解中的数据源标识
         * @Date 2020/3/18 17:40
         * @Param [clazz, method]
         * @return void
         **/
        private void resolveDataSource(Class<?> clazz, Method method) {
            try {
                Class<?>[] types = method.getParameterTypes();
                // 默认使用类型注解
                if (clazz.isAnnotationPresent(TargetDataSource.class)) {
                    TargetDataSource source = clazz.getAnnotation(TargetDataSource.class);
                    DynamicDataSourceContextHolder.setDataSource(source.value());
                }
                // 方法注解可以覆盖类型注解
                Method m = clazz.getMethod(method.getName(), types);
                if (m != null && m.isAnnotationPresent(TargetDataSource.class)) {
                    TargetDataSource source = m.getAnnotation(TargetDataSource.class);
                    DynamicDataSourceContextHolder.setDataSource(source.value());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        /**
         * @Author Jason
         * @Description 执行方法后清除数据源设置
         * @Date 2020/3/18 21:26
         * @Param [joinPoint, targetDataSource]
         * @return void
         **/
        public void afterIntercept(JoinPoint point) {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Class<?>[] types = signature.getParameterTypes();
            Class<?> target = point.getTarget().getClass();
            try {
                Method m = target.getMethod(signature.getName(), types);
                if (m != null && m.isAnnotationPresent(TargetDataSource.class)) {
                    TargetDataSource source = m.getAnnotation(TargetDataSource.class);
                    LOG.info("当前数据源"+ source.value()+ "执行清理方法");
                }
                else{
                    LOG.info("当前数据源"+ DynamicDataSourceContextHolder.get()+ "执行清理方法");
                }
    
            }catch (Exception e) {
                e.printStackTrace();
            }
            DynamicDataSourceContextHolder.clear();
        }
    }
    
    

    8、@TargetDataSource注解实现数据源切换实例

    
    import com.zqu.DataSource.TargetDataSource;
    import com.zqu.bean.Employees;
    import com.zqu.dao.IEmployeesDao;
    import com.zqu.service.IEmployeesService;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * @ClassName IEmployeesServiceImpl
     * @Description: TODO
     * @Author Jason
     * @Date 2020/3/18  18:02
     * @Version V1.0
     **/
    @Service("employeesService")
    public class IEmployeesServiceImpl implements IEmployeesService {
        @Resource(name="IEmployeesDao")
        private IEmployeesDao eDao;
        public List<Employees> findDemo(){
            return eDao.findDemo();
        }
        @TargetDataSource("dataSourceE")//切换数据源dataSourceE
        public List<Employees> find(){
            return  eDao.find();
        }
    }
    

    [参考文章](https://blog.csdn.net/johnf_nash/article/details/63260561

    )

    相关文章

      网友评论

          本文标题:SSM动态配置多数据源

          本文链接:https://www.haomeiwen.com/subject/uczcyhtx.html