精华内容
下载资源
问答
  • 多数据源切换

    2019-03-08 09:57:53
    多数据源切换,用在JAVA中链接不同数据库的切换,大大减少开发的耦合度,方便开发
  • springboot AOP 多数据源切换

    万次阅读 2019-11-07 15:08:21
    由于需要做多数据源切换,然后想到去年做过一次多数据源切换,但是之前是springmvc的,现在是springboot,两者有所不同,再次做个简单的记录。 第一步还是一样的,写一个DynamicDataSourceHolder,用来获取需要使用...

    由于需要做多数据源切换,然后想到去年做过一次多数据源切换,但是之前是springmvc的,现在是springboot,两者有所不同,在此做个简单的记录。

    第一步还是一样的,写一个DynamicDataSourceHolder,用来获取需要使用的数据源是哪个。

    public class DynamicDataSourceHolder {
        private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<>();
    
        public static String getDataSource() {
            return THREAD_DATA_SOURCE.get();
        }
    
        public static void setDataSource(String dataSource) {
            THREAD_DATA_SOURCE.set(dataSource);
        }
    }
    

    第二步再写一个DynamicDataSource类继承AbstractRoutingDataSource,并重写determineCurrentLookupKey方法。

    public class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicDataSourceHolder.getDataSource();
        }
    }
    

    第三步准备两个数据源(仅做演示效果,大家根据实际情况更改)

    spring:
      datasource:
        fop-data:
          url: 
          username: 
          password: 
        big-data:
          url: 
          username: 
          password: 
    

    将数据源注册到spring中

        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.fop-data")
        public DruidDataSource dataSource1() throws SQLException {
            DruidDataSource druidDataSource = new DruidDataSource();
            return setDruidDataSourceOther(druidDataSource);
        }
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.big-data")
        public DruidDataSource dataSource2() throws SQLException {
            DruidDataSource druidDataSource = new DruidDataSource();
            return setDruidDataSourceOther(druidDataSource);
        }
    

    第四步配置多数据源,需要注意的是:@Primary一定要加这里,缺失@Primary会导致报错,如果加到其他数据源(如dataSource1)上,会无法切换。

        @Primary
        @Bean
        public DynamicDataSource dynamicDataSource() throws SQLException {
            DynamicDataSource dataSource = new DynamicDataSource();
            Map<Object, Object> targetDataSources = new HashMap<>(16);
            targetDataSources.put("dataSource1",dataSource1());
            targetDataSources.put("dataSource2",dataSource2());
            dataSource.setTargetDataSources(targetDataSources);
            // 默认数据源
            dataSource.setDefaultTargetDataSource(dataSource1());
            return dataSource;
        }
    

    放个缺失@Primary的异常:
    Action:

    Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

    ps:起初我随便加了bean的位置,然后切不了…
    第五步添加事务管理

        @Bean
        public PlatformTransactionManager transactionManager() throws SQLException {
            return new DataSourceTransactionManager(dynamicDataSource());
        }
    

    补充下DruidDataSource数据源剩下的代码:

    private DruidDataSource setDruidDataSourceOther(DruidDataSource druidDataSource) throws SQLException {
            druidDataSource.setInitialSize(5);
            druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
            druidDataSource.setMinIdle(5);
            druidDataSource.setMaxActive(30);
            druidDataSource.setMaxWait(60000);
            druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
            druidDataSource.setMinEvictableIdleTimeMillis(300000);
            druidDataSource.setTestWhileIdle(true);
            druidDataSource.setTestOnBorrow(false);
            druidDataSource.setTestOnReturn(false);
            druidDataSource.setFilters("stat");
            return druidDataSource;
        }
    

    到这一步已经可以使用了,在访问数据前使用 DynamicDataSourceHolder.setDataSource() 做数据源切换。

    下面我们使用AOP + 注解的方式对数据源进行切换:
    首先定义注解:

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DataSource {
        DataSourceType value();
    }
    

    枚举:

    public enum DataSourceType {
        FOP_DATA("dataSource1"),
        BIG_DATA("dataSource2");
    
        public String getValue() {
            return value;
        }
    
        private String value;
        DataSourceType(String value){
            this.value = value;
        }
    }
    

    在需要切换数据源的方法上加上注解,如图:
    在这里插入图片描述
    再加上AOP切面:

    @Component
    @Aspect
    public class DataSourceAOP {
        @Before("@annotation(source)")
        public void intercept(JoinPoint point,DataSource source){
            DynamicDataSourceHolder.setDataSource(source.value().getValue());
        }
    }
    

    这个时候加了注解的可以了,但是还有个问题,当切换数据源之后,没有加注解的也使用了切换之后的数据源了,所以我又加了个默认的数据源

        /**
         * 没有加注解的默认数据源为 FOP_DATA
         */
        @Before("execution(* com.obanks.batch.job.executor.service.*.*(..))")
        public void defIntercept(){
            DynamicDataSourceHolder.setDataSource(DataSourceType.FOP_DATA.getValue());
        }
    

    更:
    不好意思,漏了个地方,还有启动类上需要排除 数据源自动配置类
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})


    有疑问还请评论区留言~

    END

    展开全文
  • 今天小编就为大家分享一篇关于Spring配置多数据源切换,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • SpringBoot多数据源切换,AOP实现动态数据源切换 操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程 或者是使用个DataSource 然后创建个SessionFactory,在使用Dao层的时候通过不同的...

    SpringBoot多数据源切换,AOP实现动态数据源切换

    操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程
    或者是使用多个DataSource 然后创建多个SessionFactory,在使用Dao层的时候通过不同的SessionFactory进行处理,不过这样的入侵性比较明显,一般的情况下我们都是使用继承HibernateSupportDao进行封装了的处理,如果多个SessionFactory这样处理就是比较的麻烦了,修改的地方估计也是蛮多的
    最后一个,也就是使用AbstractRoutingDataSource的实现类通过AOP或者手动处理实现动态的使用我们的数据源,这样的入侵性较低,非常好的满足使用的需求。比如我们希望对于读写分离或者其他的数据同步的业务场景

    ●单数据源的场景(一般的Web项目工程这样配置进行处理,就已经比较能够满足我们的业务需求)
    多数据源多SessionFactory这样的场景,估计作为刚刚开始想象想处理在使用框架的情况下处理业务,配置多个SessionFactory,然后在Dao层中对于特定的请求,通过特定的SessionFactory即可处理实现这样的业务需求,不过这样的处理带来了很多的不便之处,所有很多情况下我们宁愿直接使用封装的JDBC编程,或者使用Mybatis处理这样的业务场景

    ●使用AbstractRoutingDataSource 的实现类,进行灵活的切换,可以通过AOP或者手动编程设置当前的DataSource,不用修改我们编写的对于继承HibernateSupportDao的实现类的修改,这样的编写方式比较好,至于其中的实现原理,让我细细到来。我们想看看如何去应用,实现原理慢慢的说!

    ●编写AbstractRoutingDataSource的实现类,HandlerDataSource就是提供给我们动态选择数据源的数据的信息,我们这里编写一个根据当前线程来选择数据源,然后通过AOP拦截特定的注解,设置当前的数据源信息,也可以手动的设置当前的数据源,在编程的类中。

    /**
     * 动态数据源
     *
     * @author
     * @date 2017年3月5日 上午9:11:49
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
    	private Map<Object, Object> allResolvedDataSources = new HashMap<>();
    
    	@Override
    	protected Object determineCurrentLookupKey() {
    		return DataSourceContextUtil.getDataSourceType();
    	}
    
    	/**
    	 * 添加数据源
    	 * @param name
    	 * @param dataSource
    	 */
    	public void addDataSource(Object name,DataSource dataSource){
    		if(allResolvedDataSources.containsKey(name))
    			allResolvedDataSources.remove(name);
    		allResolvedDataSources.put(name,dataSource);
    		setTargetDataSources(allResolvedDataSources);
    	}
            /**
             * 删除数据源
             * @param name
             * @return
             */
    	    public Object deleteDataSource(Object name){
    		Object rst = null;
    		if(allResolvedDataSources.containsKey(name))
    		{
    			rst = allResolvedDataSources.get(name);
    			allResolvedDataSources.remove(name);
    			setTargetDataSources(allResolvedDataSources);
    		}
    		return rst;
    	    }
    
            /**
             * 检查数据源是否存在
             * @param name
             * @return
             */
    	    public Boolean isExistDataSource(Object name){
    	        return allResolvedDataSources.containsKey(name);
                }
    }
    

    ●设置动态选择的Datasource,这里的Set方法可以留给AOP调用,或者留给我们的具体的Dao层或者Service层中手动调用,在执行SQL语句之前。

    /**
     * 动态数据源
     *
     * @author
     * @date 2017年3月5日 上午9:11:49
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
    	private Map<Object, Object> allResolvedDataSources = new HashMap<>();
    
    	@Override
    	protected Object determineCurrentLookupKey() {
    		return DataSourceContextUtil.getDataSourceType();
    	}
    
    	/**
    	 * 添加数据源
    	 * @param name
    	 * @param dataSource
    	 */
    	public void addDataSource(Object name,DataSource dataSource){
    		if(allResolvedDataSources.containsKey(name))
    			allResolvedDataSources.remove(name);
    		allResolvedDataSources.put(name,dataSource);
    		setTargetDataSources(allResolvedDataSources);
    	}
            /**
             * 删除数据源
             * @param name
             * @return
             */
    	public Object deleteDataSource(Object name){
    		Object rst = null;
    		if(allResolvedDataSources.containsKey(name))
    		{
    			rst = allResolvedDataSources.get(name);
    			allResolvedDataSources.remove(name);
    			setTargetDataSources(allResolvedDataSources);
    		}
    		return rst;
    	}
    
            /**
             * 检查数据源是否存在
             * @param name
             * @return
             */
    	public Boolean isExistDataSource(Object name){
    	    return allResolvedDataSources.containsKey(name);
            }
    }
    
    

    ●在项目启动的时候初始化所有的数据源信息,通过DynamicDataSource类以键值对的形式存储

            DruidDataSource dataSource = dataSource();
            DruidDataSource masterDataSource = masterDataSource();
            DruidDataSource clusterDataSource = clusterDataSource();
            //DruidDataSource bizDataSource = bizDataSource();
    
            try {
                dataSource.init();
                clusterDataSource.init();
                masterDataSource.init();
    
             //   bizDataSource.init();
            }catch (SQLException sql){
                sql.printStackTrace();
            }
    
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            dynamicDataSource.addDataSource(DSEnum.DATA_SOURCE_NAME,dataSource);                     dynamicDataSource.addDataSource(DSEnum.MASTER_DATA_SOURCE_NAME,masterDataSource);
         dynamicDataSource.addDataSource(DSEnum.CLUSTER_DATA_SOURCE_NAME,clusterDataSource);
     DataSourceContextUtil.setDataSourceType(DSEnum.DATA_SOURCE_NAME);//设置默认的数据源的Key
     dynamicDataSource.setDefaultTargetDataSource(dataSource);//设置默认的数据源

    ●代码使用方法,注意的是这里使用过后需要调用清空的方法,让其返回默认的数据源,否则将会带着这个数据源满世界的报错找不到表,找不到列,映射不到类。。。。。。

    DataSourceContextUtil.setDataSourceType(DSEnum.CLUSTER_DATA_SOURCE_NAME);
            String sql2="select * from sys_config";
         List<Map<String,Object>>sqlListPage2=commonDao.getSqlListNotPage(sql2.toString(),null);
            DataSourceContextUtil.clearDataSourceType();

    下面是我使用的枚举类型,用来当做Key来使用的

    /**
     * 
     * 多数据源的枚举
     *
     * @author
     * @date 2017年3月5日 上午10:15:02
     */
    public interface DSEnum {
    
    	String DATA_SOURCE_NAME = "dataSource";		//base数据源
    
    	String MASTER_DATA_SOURCE_NAME = "masterDataSource";		//base数据源
    
    	String CLUSTER_DATA_SOURCE_NAME = "clusterDataSource";		//base数据源
    }

     

    展开全文
  • springboot+mybatis+mysql+AbstractRoutingDataSource实现多数据源切换(一写读数据源)项目中包含完整的demo案例配备文档说明,亲测可用;实现简单,代码思路清晰.
  • 本文介绍两种切换数据库的方法。 方法1:数据库信息都配置在spring xml中,适用于一般数据库切换。...方法2:将数据库信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。...

    鉴于很多同学反应,在看的过程中,自己搭建的demo,没有成功。
    首先附上本项目github地址,可以对照着看,看一下相关的配置是否正确配置。

    本文介绍两种动态切换数据库的方法。
    方法一:数据源信息配置在xml中,适用于一般数据库切换。执行完某操作,切换数据库,执行另一个操作。
    方法二:数据源信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。

    两种方法核心都是AbstractRoutingDataSource,由spring提供,用来动态切换数据源。我们需要继承它,来进行操作。本博客中,隐藏了部分spring相关的配置信息。

    源码分析可以看下一篇文章源码分析

    方法一:数据源信息都配置在xml中

    1…继承AbstractRoutingDataSource,重写determineCurrentLookupKey方法

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class MultiDataSource extends AbstractRoutingDataSource{
    
    	/* ThreadLocal,叫线程本地变量或线程本地存储。
    	 * ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
    	 * 这里使用它的子类InheritableThreadLocal用来保证父子线程都能拿到值。
    	 */
    	private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();
    	
    	/**
    	 * 设置dataSourceKey的值
    	 * @param dataSource
    	 */
    	public static void setDataSourceKey(String dataSource) {
    		dataSourceKey.set(dataSource);
    	}
    	/**
    	 * 清除dataSourceKey的值
    	 */
    	public static void toDefault() {
    		dataSourceKey.remove();
    	}
    	/**
    	 * 返回当前dataSourceKey的值
    	 */
    	@Override
    	protected Object determineCurrentLookupKey() {
    		return dataSourceKey.get();
    	}
    }
    

    2… 配置xml,这里将上面创建的MultiDataSource注入到spring容器中,这里主要用到AbstractRoutingDataSource的两个属性defaultTargetDataSource和targetDataSources。defaultTargetDataSource默认目标数据源,targetDataSources(map类型)存放用来切换的数据源。配置完以后,其他地方用到数据源的话,都引用multiDataSource。

    <?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:context="http://www.springframework.org/schema/context"
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
    
    
    	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
    		init-method="init" destroy-method="close">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    			<property name="url" value="第一个数据库的url" />
    			<property name="username" value="账号" />
    			<property name="password" value="密码" />
    			<property name="validationQuery" value="select 1"></property>
    			<property name="testOnBorrow" value="true"></property>
    	</bean>
    		<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource"
    		init-method="init" destroy-method="close">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    		<property name="url" value="第二个数据库的url" />
    		<property name="username" value="账号" />
    		<property name="password" value="密码" />
    		<property name="validationQuery" value="select 1"></property>
    		<property name="testOnBorrow" value="true"></property>
    	</bean>
    	<bean id="multiDataSource" class="com.wbb.dataSource.MultiDataSource">
    		<property name="defaultTargetDataSource" ref="dataSource"></property>
    		<property name="targetDataSources">
    			<map>
    				<entry key="dataSource2" value-ref="dataSource2"></entry>
    			</map>
    		</property>
    	</bean>
    	<!-- mybatis -->
    	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    		<property name="dataSource" ref="multiDataSource"></property>
    		<property name="configLocation" value="classpath:mybatis-config.xml"></property>
    		<property name="mapperLocations" value="classpath:com/wbb/mapper/*.xml"></property>
    	</bean>
    	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    		<property name="basePackage" value="com.wbb.mapper"></property>
    	</bean>
    
    	<!-- 添加事务管理 -->
    	<tx:annotation-driven transaction-manager="transactionManager" />
    
    	<bean id="transactionManager"
    		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="multiDataSource"></property>
    	</bean>
    
    </beans>
    

    3…手动切换数据源,切换完以后,记得再切回默认数据库。

    MultiDataSource.setDataSourceKey("dataSource2");//切换到dataSource2数据源
    XXX在该数据源下的操作XXX
    MultiDataSource.toDefault();//操作完以后,清除dataSourceKey的值,即切回默认数据源,原理后面会讲。
    

    4… 利用aop切换数据源,这里记得开启aop,配置文件中使用<aop:aspectj-autoproxy />
    4.1首先定义一个注解,来调用注解切换数据库

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DynamicRoutingDataSource {
    
    	String value() default "dataSource";//本文默认dataSource
    
    }
    

    4.2 这里使用@Before和@After,在调用目标方法前,进行aop拦截,通过解析注解上的值来切换数据源。在调用方法结束后,切回默认数据源。如果目标方法无返回值,也可以使用@Around,调用ProceedingJoinPoint的proceed()方法前切换数据源,调用proceed()方法后切回默认数据源。

    import java.lang.reflect.Method;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    import com.wbb.annotation.DynamicRoutingDataSource;
    import com.wbb.dataSource.MultiDataSource;
    
    @Aspect
    @Component
    public class HandlerDataSourceAop {
    
    	private static Logger logger = LoggerFactory.getLogger(HandlerDataSourceAop.class);
    	/**
    	 * @within匹配类上的注解
    	 * @annotation匹配方法上的注解
    	 */
    	@Pointcut("@within(com.wbb.annotation.DynamicRoutingDataSource)||@annotation(com.wbb.annotation.DynamicRoutingDataSource)")
    	public void pointcut(){}
    	
    	@Before(value = "pointcut()")
    	public void beforeOpt(JoinPoint joinPoint) {
    	/** 先查找方法上的注解,没有的话再去查找类上的注解
    	*-----------------------------------------------------------------------
     	* 这里使用的是接口的模式,注解在实现类上,所以不能使用如下方式获取目标方法的对象,
    	* 因为该方式获取的是该类的接口或者顶级父类的方法的对象.
    	* MethodSignature methodSignature = (MethodSignature)point.getSignature();
         * Method method = methodSignature.getMethod();
         * DynamicRoutingDataSource annotation = method.getAnnotation(DynamicRoutingDataSource.class);
         * 通过上面代码是获取不到方法上的注解的,如果真要用上面代码来获取,可以修改aop代理模式,修改为cglib代理
         * 在xml配置文件修改为<aop:aspectj-autoproxy proxy-target-class="true" /> ,
         * proxy-target-class属性true为cglib代理,默认false为jdk动态代理 。
         * ---------------------------------------------------------
         * 本文使用是jdk动态代理, 这里使用反射的方式获取方法
    	*/
    	//反射获取Method 方法一
    	Object target = joinPoint.getTarget();
    	Class<?> clazz = target.getClass();
    	Method[] methods = clazz.getMethods();
    	DynamicRoutingDataSource annotation = null;
    	for (Method method : methods) {
    		if (joinPoint.getSignature().getName().equals(method.getName())) {
    			annotation = method.getAnnotation(DynamicRoutingDataSource.class);
    			if (annotation == null) {
    				annotation = joinPoint.getTarget().getClass().getAnnotation(DynamicRoutingDataSource.class);
    				if (annotation == null) {
    					return;
    				}
    			}
    		}
    	}
    	// 	反射获取Method 方法二
    	//		Object[] args = joinPoint.getArgs();
    	//		Class<?>[] argTypes = new Class[joinPoint.getArgs().length];
    	//		for (int i = 0; i < args.length; i++) {
    	//			argTypes[i] = args[i].getClass();
    	//		}
    	//		Method method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), argTypes);
    	//		DynamicRoutingDataSource annotation = method.getAnnotation(DynamicRoutingDataSource.class);
    	//		if (annotation == null) {
    	//			annotation = joinPoint.getTarget().getClass().getAnnotation(DynamicRoutingDataSource.class);
    	//			if (annotation == null) {
    	//				return;
    	//			}
    	//		}
    		
    		String dataSourceName = annotation.value();
    		MultiDataSource.setDataSourceKey(dataSourceName);
    		logger.info("切到" + dataSourceName + "数据库");
    	}
    	@After(value="pointcut()")
    	public void afterOpt(){
    		MultiDataSource.toDefault();
    		logger.info("切回默认数据库");
    	}
    }
    

    4.3 使用:只需要把@DynamicRoutingDataSource注解加到方法或者类上即可

    @DynamicRoutingDataSource("dataSource2")
    

    方法一到此就结束了

    方法二:数据源信息配置在数据库中

    1.xml的配置,这里只需要配置一个默认的数据源就行了,因为其他的数据源都是从该数据源的数据源表中读取。

    	<?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:context="http://www.springframework.org/schema/context"
    		xmlns:tx="http://www.springframework.org/schema/tx"
    		xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
    	
    		<bean id="defaultDataSource" class="com.alibaba.druid.pool.DruidDataSource"
    			init-method="init" destroy-method="close">
    			<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    			<property name="url" value="第一个数据库的url" />
    			<property name="username" value="账号" />
    			<property name="password" value="密码" />
    			<property name="validationQuery" value="select 1"></property>
    			<property name="testOnBorrow" value="true"></property>
    		</bean>
    	
    		<!--动态数据源相关-->
    	    <bean id="dynamicDataSource" class="com.wbb.dataSource.dynamic.DynamicDataSource">
    	        <property name="targetDataSources">
    	            <map key-type="java.lang.String">
    	                <entry key="defaultDataSource" value-ref="defaultDataSource"/>
    	            </map>
    	        </property>
    	        <property name="defaultTargetDataSource" ref="defaultDataSource"/>
    	    </bean>
    		<!-- mybatis -->
    		<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    			<property name="dataSource" ref="dynamicDataSource"></property>
    			<property name="configLocation" value="classpath:mybatis-config.xml"></property>
    			<property name="mapperLocations" value="classpath:com/wbb/mapper/*.xml"></property>
    		</bean>
    		<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    			<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    			<property name="basePackage" value="com.wbb.mapper"></property>
    		</bean>
    	
    		<!-- 添加事务管理 -->
    		<tx:annotation-driven transaction-manager="transactionManager" />
    	
    		<bean id="transactionManager"
    			class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    			<property name="dataSource" ref="dynamicDataSource"></property>
    		</bean>
    	
    	</beans>
    

    2.数据源表的设计

    DROP TABLE IF EXISTS `other_datasource`;
    CREATE TABLE `other_datasource` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `datasource_name` varchar(64) DEFAULT NULL,
      `database_ip` varchar(64) DEFAULT NULL,
      `database_port` varchar(64) DEFAULT NULL,
      `database_name` varchar(64) DEFAULT NULL,
      `database_username` varchar(64) DEFAULT NULL,
      `database_password` varchar(64) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
    

    这里写图片描述
    3.数据源表对应的bean

    /**
     * 数据源bean构建器
     */
    public class DataSourceBean {
    
    	private final String beanName;		//注册在spring中bean名字
    	private final String driverClassName; 
    	private final String url;
    	private final String username;
    	private final String password;
    	private final String validationQuery;
    	private final Boolean testOnBorrow;
    
    	public String getBeanName() {
    		return beanName;
    	}
    
    	public String getDriverClassName() {
    		return driverClassName;
    	}
    
    	public String getUrl() {
    		return url;
    	}
    
    	public String getUsername() {
    		return username;
    	}
    
    	public String getPassword() {
    		return password;
    	}
    
    	public String getValidationQuery() {
    		return validationQuery;
    	}
    
    	public Boolean getTestOnBorrow() {
    		return testOnBorrow;
    	}
    
    	public DataSourceBean(DataSourceBeanBuilder builder) {
    		this.beanName = builder.getBeanName();
    		this.driverClassName = builder.getDriverClassName();
    		this.url = builder.getUrl();
    		this.username = builder.getUsername();
    		this.password = builder.getPassword();
    		this.validationQuery = builder.getValidationQuery();
    		this.testOnBorrow = builder.getTestOnBorrow();
    	}
    
    	public static class DataSourceBeanBuilder {
    		private String beanName;
    		private String driverClassName = "com.mysql.jdbc.Driver";
    		private String url = "jdbc:mysql://%s:%s/%s";
    	    private String databaseIP;
    	    private String databasePort;
    	    private String databaseName;
    		private String username;
    		private String password;
    		private String validationQuery = "select 1";
    		private Boolean testOnBorrow = true;
    		public DataSourceBeanBuilder(String beanName, String databaseIP, String databasePort, String databaseName,
    				String username, String password) {
    			super();
    			this.beanName = beanName;
    			this.databaseIP = databaseIP;
    			this.databasePort = databasePort;
    			this.databaseName = databaseName;
    			this.username = username;
    			this.password = password;
    		}
    		public DataSourceBeanBuilder() {
    			super();
    		}
    		public DataSourceBeanBuilder driverClassName(String driverClassName) {
    			this.driverClassName = driverClassName;
    			return this;
    		}
    		public DataSourceBeanBuilder validationQuery(String validationQuery) {
    			this.validationQuery = validationQuery;
    			return this;
    		}
    		public DataSourceBeanBuilder testOnBorrow(Boolean testOnBorrow) {
    			this.testOnBorrow = testOnBorrow;
    			return this;
    		}
    		public String getUrl() {
    			return String.format(url,this.databaseIP,this.databasePort,this.databaseName);
    		}
    		public String getBeanName() {
    			return beanName;
    		}
    		public String getDriverClassName() {
    			return driverClassName;
    		}
    		public String getDatabaseIP() {
    			return databaseIP;
    		}
    		public String getDatabasePort() {
    			return databasePort;
    		}
    		public String getDatabaseName() {
    			return databaseName;
    		}
    		public String getUsername() {
    			return username;
    		}
    		public String getPassword() {
    			return password;
    		}
    		public String getValidationQuery() {
    			return validationQuery;
    		}
    		public Boolean getTestOnBorrow() {
    			return testOnBorrow;
    		}
    	}
    

    4.这里将操作抽离出,建立数据源操作类,方便操作

    /**
     * 数据源操作类
     */
    public class DataSourceContext {
    
    	private static ThreadLocal<DataSourceBean> threadLocal = new InheritableThreadLocal<DataSourceBean>();
    
    	/**
    	 * 获取数据源
    	 */
    	public static DataSourceBean getDataSource() {
    		return threadLocal.get();
    	}
    	/**
    	 * 设置数据源
    	 */
    	public static void setDataSource(DataSourceBean dataSourceBean) {
    		threadLocal.set(dataSourceBean);
    	}
    	/**
    	 * 清除数据源
    	 * 清除后,数据源为默认时间
    	 */
    	public static void toDefault() {
    		threadLocal.remove();
    	}
    }
    
    

    5.定义AbstractRoutingDataSource的继承类DynamicDataSource,同时实现ApplicationContextAware接口,因为该方法切换数据源,需要到spring上下文中生成和获取数据源bean。

    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    import com.alibaba.druid.pool.DruidDataSource;
    
    
    public class  DynamicDataSource extends AbstractRoutingDataSource implements ApplicationContextAware{
    
    	private ApplicationContext applicationContext ;
    
    	/**
    	 * 连接数据源前,调用该方法
    	 */
    	@Override
    	protected Object determineCurrentLookupKey() {
    		//1.获取手动设置的数据源参数DataSourceBean
    		DataSourceBean dataSourceBean = DataSourceContext.getDataSource();
    		if(dataSourceBean == null) {
    			return null;
    		}
    		try {
    			//2.获取AbstractRoutingDataSource的targetDataSources属性,该属性存放数据源属性
    			Map<Object, Object> targetSourceMap = getTargetSource();
    			synchronized(this) {
    				/*
    				 * 3.判断targetDataSources中是否已经存在要设置的数据源bean
    				 * 存在的话,则直接返回beanName
    				 * 
    				 */
    				if(!targetSourceMap.keySet().contains(dataSourceBean.getBeanName())) {
    					/*不存在,则进行以下几步
    					3.1 先在spring容器中创建该数据源bean
    					*/
    					Object dataSource = createDataSource(dataSourceBean);
    					//3.2 在创建后的bean,放入到targetDataSources Map中
    					targetSourceMap.put(dataSourceBean.getBeanName(), dataSource);
    					/*
    					 * 3.3 通知spring有bean更新
    					 * 主要更新AbstractRoutingDataSource的resolvedDefaultDataSource(Map)属性,
    					 * 更新完以后,AbstractRoutingDataSource的determineTargetDataSource()中,才能找到数据源
    					 * 代码如下:
    					 * Object lookupKey = determineCurrentLookupKey();
    					   DataSource dataSource = this.resolvedDataSources.get(lookupKey);
    					 */
    					super.afterPropertiesSet();
    				}
    			}
    			for(Map.Entry<Object, Object> entry : targetSourceMap.entrySet()) {
    				System.out.println(entry.getKey()+"-"+entry.getValue());
    			}
    			return dataSourceBean.getBeanName();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    	/**
    	 * 根据数据源信息在spring中创建bean,并返回
    	 * @param dataSourceBean 数据源信息
    	 * @return
    	 * @throws IllegalAccessException
    	 */
    	public Object createDataSource(DataSourceBean dataSourceBean) throws IllegalAccessException {
    		//1.将applicationContext转化为ConfigurableApplicationContext
    		ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
    		//2.获取bean工厂并转换为DefaultListableBeanFactory
    		DefaultListableBeanFactory beanFactory =  (DefaultListableBeanFactory) context.getBeanFactory();
    		/*
    		 * 3.本文用的是DruidDataSource,所有在这里我们获取的是该bean的BeanDefinitionBuilder,
    		 * 通过BeanDefinitionBuilder来创建bean定义
    		 */
    		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class);
    		/**
    		 * 4.获取DataSourceBean里的属性和对应值,并将其交给BeanDefinitionBuilder创建bean的定义
    		 */
    		Map<String, Object> propertyKeyValues = getPropertyKeyValues(DataSourceBean.class, dataSourceBean);
    		for(Map.Entry<String,Object> entry : propertyKeyValues.entrySet()) {
    			beanDefinitionBuilder.addPropertyValue(entry.getKey(), entry.getValue());
    		}
    		//5.bean定义创建好以后,将其交给beanFactory注册成bean对象,由spring容器管理
    		beanFactory.registerBeanDefinition(dataSourceBean.getBeanName(), beanDefinitionBuilder.getBeanDefinition());
    		//6.最后获取步骤5生成的bean,并将其返回
    		return context.getBean(dataSourceBean.getBeanName());
    	}
    	//获取类属性和对应的值,放入Map中
        @SuppressWarnings("unused")
    	private <T> Map<String, Object> getPropertyKeyValues(Class<T> clazz, Object object) throws IllegalAccessException {
           Field[] fields = clazz.getDeclaredFields();
           Map<String,Object> map = new HashMap<>();
           for (Field field : fields) {
        	   field.setAccessible(true);
        	   map.put(field.getName(), field.get(object));
           }
           map.remove("beanName");
           return map;
        }
        //通过反射获取AbstractRoutingDataSource的targetDataSources属性
    	@SuppressWarnings("unchecked")
    	public Map<Object, Object> getTargetSource() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
    		Field field = AbstractRoutingDataSource.class.getDeclaredField("targetDataSources");
    		field.setAccessible(true);
    		return (Map<Object, Object>) field.get(this);
    	}
    	@Override
    	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    			this.applicationContext = applicationContext;
    	}
     
    }
    
    

    6.测试用例测试一下

    	@Test
    	public void testDynamicDataSource() {
    		1.从表中获取需要切换的数据源信息
    		List<DataSourceDO> dataSourceDOList = dataSourceMapper.getAllDataSources();
    		2.循环遍历,切换数据源,执行操作,切完就得切回默认数据源
    		for (DataSourceDO dataSourceDO : dataSourceDOList) {
    			DataSourceBean dataSourceBean = new DataSourceBean(new DataSourceBeanBuilder(dataSourceDO.getDatasourceName(),
    					dataSourceDO.getDatabaseIp(), dataSourceDO.getDatabasePort(), dataSourceDO.getDatabaseName(),
    					dataSourceDO.getUsername(), dataSourceDO.getPassword()));
    			DataSourceContext.setDataSource(dataSourceBean);
    			XXX你的操作XXX
    			DataSourceContext.toDefault();
    		}
    	}
    

    方法二部分参考https://blog.csdn.net/yizhenn/article/details/53965552该博客的思想。

    源码分析可以看下一篇文章https://blog.csdn.net/u013034378/article/details/81661706

    讲到这里,本篇文章到此也就结束了,如果文章中有问题,或者有一些不够严谨完善的地方,希望大家体谅体谅。欢迎大家留言,交流交流。
    最后附上本项目github的地址

    展开全文
  • 主要介绍了使用Spring的AbstractRoutingDataSource实现多数据源切换示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
  • SpringBoot使用AOP完成多数据源切换,下载后导入即可使用,JDK1.8
  • 本篇文章主要介绍了SpringBoot AOP方式实现多数据源切换的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了Spring与Mybatis相结合实现多数据源切换功能的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
  • springboot多数据源切换mysql+sql server,该事例代码demo,下载下来就能使用,使用工具idea,自行安装mysql和sql server
  • 本项目是:SpringBoot 基于注解,实现多数据源切换(辅助用到:Druid连接池 + 事务 + MyBatis 等),项目下载后只需要修改一下.yml文件中的 MySQL 的URL连接,即可成功启动,欢迎大家下载
  • spring + druid 配置动态配置数据源以及多数据源切换功能实现 数据源连接池使用druid 其他的数据源基本原理相同 spring中配置默认数据源连接池如下: <!-- 数据源配置, 使用 BoneCP 数据库连接池 --> &...

    spring + druid 配置动态配置数据源以及多数据源切换功能实现

     

    数据源连接池使用druid 其他的数据源基本原理相同

    spring中配置默认数据源连接池如下:

    <!-- 数据源配置, 使用 BoneCP 数据库连接池 -->
        <bean id="dataSourceOne" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
            <property name="name" value="dataSourceOne"/>
            <!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass -->
            <property name="driverClassName" value="${jdbc.driver}" />
            
            <!-- 基本属性 url、user、password -->
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
            
            <!-- 配置初始化大小、最小、最大 -->
            <property name="initialSize" value="${jdbc.pool.init}" />
            <property name="minIdle" value="${jdbc.pool.minIdle}" /> 
            <property name="maxActive" value="${jdbc.pool.maxActive}" />
            
            <!-- 配置获取连接等待超时的时间 -->
            <property name="maxWait" value="60000" />
            
            <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
            <property name="timeBetweenEvictionRunsMillis" value="60000" />
            
            <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
            <property name="minEvictableIdleTimeMillis" value="300000" />
            
            <property name="validationQuery" value="${jdbc.testSql}" />
            <property name="testWhileIdle" value="true" />
            <property name="testOnBorrow" value="false" />
            <property name="testOnReturn" value="false" />
            
            <!-- 打开PSCache,并且指定每个连接上PSCache的大小(Oracle使用)
            <property name="poolPreparedStatements" value="true" />
            <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> -->
            
            <!-- 配置监控统计拦截的filters -->
            <property name="filters" value="stat" /> 
        </bean>

    接下来配置多数据源bean

    <!-- 多数据源配置 -->
         <bean id="dynamicDataSource" class="com.XXX.datasource.DynamicDataSource" >  
            <property name="targetDataSources">  
                <map>  
                    <entry value-ref="dataSourceOne" key="dataSourceOne"></entry>  
    
                    <!--此处是对数据源的引用-->
                   <!--  <entry value-ref="dataSourceTow" key="dataSourceTow"></entry> -->
                </map>  
            </property>  
            <property name="defaultTargetDataSource" ref="dataSourceOne" />  
            <property name="debug"  value="true"/>
        </bean> 

    这个类 com.XXX.datasource.DynamicDataSource 需要手动创建

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;
    
    import javax.sql.DataSource;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    import org.springframework.util.StringUtils;
    
    import com.alibaba.druid.pool.DruidConnectionHolder;
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    import com.alibaba.druid.pool.DruidPooledConnection;
    import com.alibaba.druid.stat.DruidDataSourceStatManager;
    import com.alibaba.druid.util.DruidDataSourceUtils;
    
    /**
     * @author zh
     */
    public class DynamicDataSource extends AbstractRoutingDataSource{
    
        private boolean debug = false;
        Logger log = LoggerFactory.getLogger(this.getClass());
        private Map<Object, Object> dynamicTargetDataSources;
    
        private Object dynamicDefaultTargetDataSource;
        /* (non-Javadoc)
    @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
         */
        @Override
        protected Object determineCurrentLookupKey() {
             String datasource=DBContextHolder.getDataSource();
             if(debug)
             {
                 if(StringUtils.isEmpty(datasource)){
                    log.info("---当前数据源:默认数据源---"); 
                 }else{
                     log.info("---当前数据源:"+datasource+"---"); 
                 }
             }
            
             return datasource;
        }
        
        @Override
        public void setTargetDataSources(Map<Object, Object> targetDataSources) {
            super.setTargetDataSources(targetDataSources);
            this.dynamicTargetDataSources = targetDataSources;
        }
        //创建数据源
        public boolean createDataSource(String key,String driveClass,String url,String username,String password){
            try {
                try {    //排除连接不上的错误
                    Class.forName(driveClass);  
                    DriverManager.getConnection(url, username, password);
                } catch (Exception e) {
                    return false;
                }
                @SuppressWarnings("resource")
                DruidDataSource druidDataSource = new DruidDataSource();
                druidDataSource.setName(key);
                druidDataSource.setDriverClassName(driveClass);
                druidDataSource.setUrl(url);
                druidDataSource.setUsername(username);
                druidDataSource.setPassword(password);
                druidDataSource.setMaxWait(60000);
                druidDataSource.setFilters("stat");
                DataSource createDataSource = (DataSource)druidDataSource;
                druidDataSource.init();
                Map<Object, Object> dynamicTargetDataSources2 =  this.dynamicTargetDataSources;
                dynamicTargetDataSources2.put(key, createDataSource);//加入map
                setTargetDataSources(dynamicTargetDataSources2);//将map赋值给父类的TargetDataSources
                super.afterPropertiesSet();//将TargetDataSources中的连接信息放入resolvedDataSources管理
                return true;
            } catch (Exception e) {
                log.error(e+"");
                return false;
            }
        }
        //删除数据源
        public boolean delDatasources(String datasourceid){
            Map<Object, Object> dynamicTargetDataSources2 =  this.dynamicTargetDataSources;
            if(dynamicTargetDataSources2.containsKey(datasourceid)){
                Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
                for(DruidDataSource l:druidDataSourceInstances){
                    if(datasourceid.equals(l.getName())){
                        System.out.println(l);
                        dynamicTargetDataSources2.remove(datasourceid);
                        DruidDataSourceStatManager.removeDataSource(l);
                        setTargetDataSources(dynamicTargetDataSources2);//将map赋值给父类的TargetDataSources
                        super.afterPropertiesSet();//将TargetDataSources中的连接信息放入resolvedDataSources管理
                        return true;
                    }
                }
                return false;
            }else{
                return false;
            }
        }
        
        //测试数据源连接是否有效
        public boolean testDatasource(String key,String driveClass,String url,String username,String password){
            try {
                Class.forName(driveClass);  
                DriverManager.getConnection(url, username, password);  
                return true;
            } catch (Exception e) {
                return false;
            }
        }
        /**
         * Specify the default target DataSource, if any.
         * <p>The mapped value can either be a corresponding {@link javax.sql.DataSource}
         * instance or a data source name String (to be resolved via a
         * {@link #setDataSourceLookup DataSourceLookup}).
         * <p>This DataSource will be used as target if none of the keyed
         * {@link #setTargetDataSources targetDataSources} match the
         * {@link #determineCurrentLookupKey()} current lookup key.
         */
        public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
            super.setDefaultTargetDataSource(defaultTargetDataSource);
            this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
        }
        /**
         * @param debug the debug to set
         */
        public void setDebug(boolean debug) {
            this.debug = debug;
        }
    
        /**
         * @return the debug
         */
        public boolean isDebug() {
            return debug;
        }
    
        /**
         * @return the dynamicTargetDataSources
         */
        public Map<Object, Object> getDynamicTargetDataSources() {
            return dynamicTargetDataSources;
        }
    
        /**
         * @param dynamicTargetDataSources the dynamicTargetDataSources to set
         */
        public void setDynamicTargetDataSources(
                Map<Object, Object> dynamicTargetDataSources) {
            this.dynamicTargetDataSources = dynamicTargetDataSources;
        }
    
        /**
         * @return the dynamicDefaultTargetDataSource
         */
        public Object getDynamicDefaultTargetDataSource() {
            return dynamicDefaultTargetDataSource;
        }
        
        /**
         * @param dynamicDefaultTargetDataSource the dynamicDefaultTargetDataSource to set
         */
        public void setDynamicDefaultTargetDataSource(
                Object dynamicDefaultTargetDataSource) {
            this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource;
        }
        
    }
    

    其中该类继承了spring的AbstractRoutingDataSource 查看其源码,发现所有的数据源都是通过

    afterPropertiesSet() 将存放在targetDataSources 这个Map中的数据源赋值给resolvedDataSources

    对象的,spring是从resolvedDataSources对象中获取数据源对象的,我们能需要把自己的数据源放入

    resolvedDataSources这个Map中就ok了。

    接下来创建数据源切换工具类

    /**
     * 数据源切换
     * @author zh
     *
     */
    public class DBContextHolder {
         private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
         
             //调用此方法,切换数据源
            public static void setDataSource(String dataSource) {  
                contextHolder.set(dataSource);  
            }  
              
            public static String getDataSource() {  
                return contextHolder.get();  
            }  
              
            public static void clearDataSource() {  
                contextHolder.remove();  
            }  
    }

    具体实动态新增数据源,需要创建数据库用以存储 数据库连接信息,以及数据源key信息。

    初始话数据库连接数据源,可以使用spring监听 实现ApplicationListener即可,如下

    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;
    
    import com.casking.cdds.modules.datasource.entity.CNDatasources;
    
    public class InitDatasourcesLS implements ApplicationListener<ApplicationEvent>{
    
        @Autowired
        private CNDatasourcesService service;
        
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            List<CNDatasources> list = service.findList(new CNDatasources());
            for(CNDatasources li:list){
    
                //这里调用创建数据源的方法即可
                service.addDataSourceDynamic(li.getDatasource(),li);
            }
        }
    
    }
    展开全文
  • SpringBoot 多数据源切换

    千次阅读 2019-06-29 20:40:01
    SpringBoot多数据源切换 目的: 1)读写库在代码端实现 2)需要操作数据库的时候需要使用 核心类 核心类AbstractRoutingDataSource,这个类是一个抽象化的DataSource类。可以用来选择个数据源,确认数据源。 ...
  • 下面小编就为大家带来一篇解决spring mvc 多数据源切换,不支持事务控制的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • springboot +hive+mysql 多数据源切换 个人写了一个demo 并测试通过 下载地址
  • 踩坑实录 亲测可用的springboot 整合mybatis druid 多数据源切换方案 使用注解方式更加灵活
  • Springboot MyBatis多数据源切换

    千次阅读 2017-04-09 11:45:23
    Springboot MyBatis多数据源切换在实际开发中,我们一个项目可能会用到个数据库,通常一个数据库对应一个数据源。本示例,通过两种方式实现多数据源切换 1)手动切换 2)使用注解进行切换代码结构: 简要原理:...
  • Spring Boot Mybaits mybatis基本配置、多数据源切换、动态加载数据源
  • 本文的源码分析是基于上一篇文章多数据源切换https://blog.csdn.net/u013034378/article/details/81455513,本文说的方法一,方法二,指的就是上一篇文章里的两种切换数据源的方法。 1.AbstractRoutingDataSource...
  • spring+mybatis 实现多数据源切换

    千次阅读 2018-05-28 20:11:17
    背景 相信大家在开发工作中肯定遇到过一个应用可能需要连接...下面讲解利用spring+mybatis 实现多数据源切换,话不说直接上代码。 jdbc和log4j的配置 log4j.properties #定义输出格式 ConversionPattern=%d ...
  • Mybatis+Spring+SpringMVC+quartz多数据源切换 定时任务 数据库之间同步
  • mybatisplus多数据源切换 踩坑笔记

    千次阅读 2019-09-12 10:05:21
    继上一篇关于多数据源切换https://blog.csdn.net/qq_42170268/article/details/100120520,原来的需求是两个数据库表结构会有所不同,现在的需求变成不同数据库的要同步的两张表结构相同,再mybatisplus的基础利用...
  • 最近项目需要指出多数据源,同时支持事务回滚,这里记录一下 1、多数据源方式介绍 主要方式有以下两种: 通过配置个SqlSessionFactory 来实现多数据源,这么做的话,未免过于笨重,而且无法实现动态添加数据源...
  • 说明:之前写的这篇springboot+aop+自定义注解,实现多数据源切换(通用版)经过测试有些发现事务无法管理,有时候切换数据源容易失败,现在用一种更靠谱的方式来实现多数据源切换。 一.引入相应的maven依赖 <...
  • spring+druid+AtomikosDataSource实现多数据源切换及分布式事务控制
  • 1,先看个目录结构图可以看到,我把要设置的配置文件都放在了config文件夹下面2,Application.java是程序启动项,里面必须设置3,application.properties是多数据源切换的配置文件好,接下来开始进行数据源的切换...
  • spring-boot集成mybatis+druid实现 hive/mysql多数据源切换,用mysql数据库作为用户验证库以及用户信息库,hive作为数据可视化源库。
  • Spring AOP实现多数据源切换

    千次阅读 2018-11-30 22:44:07
    有时候我们会遇到这样的场景:一个应用系统中存在数据源,需要根据不同业务场景进行临时切换。比如读写分离(也可以考虑使用Mycat等数据库中间件)等。 Spring提供了动态数据源的功能,可以让我们实现在对...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 299,798
精华内容 119,919
关键字:

多数据源切换