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

    2017-11-01 15:13:22
    数据源动态切换,主要框架springboot、mybatis、druid
  • 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 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

    展开全文
  • AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求。一般的情况下我们都是使用SSH或者SSM框架进行处理...

    AbstractRoutingDataSource动态数据源切换

    上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求。一般的情况下我们都是使用SSH或者SSM框架进行处理我们的数据源的信息。
    操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程(http://blog.csdn.net/yanzi1225627/article/details/26950615/
    或者是使用多个DataSource 然后创建多个SessionFactory,在使用Dao层的时候通过不同的SessionFactory进行处理,不过这样的入侵性比较明显,一般的情况下我们都是使用继承HibernateSupportDao进行封装了的处理,如果多个SessionFactory这样处理就是比较的麻烦了,修改的地方估计也是蛮多的
    最后一个,也就是使用AbstractRoutingDataSource的实现类通过AOP或者手动处理实现动态的使用我们的数据源,这样的入侵性较低,非常好的满足使用的需求。比如我们希望对于读写分离或者其他的数据同步的业务场景

    • 下面看看图片
      这里写图片描述

    • 单数据源的场景(一般的Web项目工程这样配置进行处理,就已经比较能够满足我们的业务需求)

    • 多数据源多SessionFactory这样的场景,估计作为刚刚开始想象想处理在使用框架的情况下处理业务,配置多个SessionFactory,然后在Dao层中对于特定的请求,通过特定的SessionFactory即可处理实现这样的业务需求,不过这样的处理带来了很多的不便之处,所有很多情况下我们宁愿直接使用封装的JDBC编程,或者使用Mybatis处理这样的业务场景
    • 使用AbstractRoutingDataSource 的实现类,进行灵活的切换,可以通过AOP或者手动编程设置当前的DataSource,不用修改我们编写的对于继承HibernateSupportDao的实现类的修改,这样的编写方式比较好,至于其中的实现原理,让我细细到来。我们想看看如何去应用,实现原理慢慢的说!

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

    package com.common.utils.manydatasource;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    /**
     * descrption: 多数据源的选择
     * authohr: wangji
     * date: 2017-08-21 10:32
     */
    public class MultipleDataSourceToChoose extends AbstractRoutingDataSource {
    
        /**
         * @desction: 根据Key获取数据源的信息,上层抽象函数的钩子
         * @author: wangji
         * @date: 2017/8/21
         * @param:
         * @return:
         */
        @Override
        protected Object determineCurrentLookupKey() {
            return HandlerDataSource.getDataSource();
        }
    }
    
    • 设置动态选择的Datasource,这里的Set方法可以留给AOP调用,或者留给我们的具体的Dao层或者Service层中手动调用,在执行SQL语句之前。
    package com.common.utils.manydatasource;
    
    /**
     * descrption: 根据当前线程来选择具体的数据源
     * authohr: wangji
     * date: 2017-08-21 10:36
     */
    public class HandlerDataSource {
    
        private static ThreadLocal<String> handlerThredLocal = new ThreadLocal<String>();
    
        /**
         * @desction: 提供给AOP去设置当前的线程的数据源的信息
         * @author: wangji
         * @date: 2017/8/21
         * @param: [datasource]
         * @return: void
         */
        public static void putDataSource(String datasource) {
            handlerThredLocal.set(datasource);
        }
    
        /**
         * @desction: 提供给AbstractRoutingDataSource的实现类,通过key选择数据源
         * @author: wangji
         * @date: 2017/8/21
         * @param: []
         * @return: java.lang.String
         */
        public static String getDataSource() {
            return handlerThredLocal.get();
        }
    
        /**
         * @desction: 使用默认的数据源
         */
        public static void clear() {
            handlerThredLocal.remove();
        }
    }
    
    • 设置拦截数据源的注解,可以设置在具体的类上,或者在具体的方法上,dataSource是当前数据源的一个别名用于标识我们的数据源的信息。
    package com.common.utils.manydatasource;
    
    import java.lang.annotation.*;
    
    /**
     * @description: 创建拦截设置数据源的注解
     * Created by wangji on 2017/8/21.
     */
    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DynamicSwitchDataSource {
    
        String dataSource() default "";
    }
    
    • AOP拦截类的实现,通过拦截上面的注解,在其执行之前处理设置当前执行SQL的数据源的信息,HandlerDataSource.putDataSource(….),这里的数据源信息从我们设置的注解上面获取信息,如果没有设置就是用默认的数据源的信息。
    package com.common.utils.manydatasource;
    
    import lombok.extern.slf4j.Slf4j;
    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.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * descrption: 使用AOP拦截特定的注解去动态的切换数据源
     * authohr: wangji
     * date: 2017-08-21 10:42
     */
    @Aspect
    @Slf4j
    @Component
    @Order(1)
    public class HandlerDataSourceAop {
        //@within在类上设置
        //@annotation在方法上进行设置
        @Pointcut("@within(com.common.utils.manydatasource.DynamicSwitchDataSource)||@annotation(com.common.utils.manydatasource.DynamicSwitchDataSource)")
        public void pointcut() {}
    
        @Before("pointcut()")
        public void doBefore(JoinPoint joinPoint)
        {
            Method method = ((MethodSignature)joinPoint.getSignature()).getMethod();
            DynamicSwitchDataSource annotationClass = method.getAnnotation(DynamicSwitchDataSource.class);//获取方法上的注解
            if(annotationClass == null){
                annotationClass = joinPoint.getTarget().getClass().getAnnotation(DynamicSwitchDataSource.class);//获取类上面的注解
                if(annotationClass == null) return;
            }
            //获取注解上的数据源的值的信息
            String dataSourceKey = annotationClass.dataSource();
            if(dataSourceKey !=null){
                //给当前的执行SQL的操作设置特殊的数据源的信息
                HandlerDataSource.putDataSource(dataSourceKey);
            }
            log.info("AOP动态切换数据源,className"+joinPoint.getTarget().getClass().getName()+"methodName"+method.getName()+";dataSourceKey:"+dataSourceKey==""?"默认数据源":dataSourceKey);
        }
    
        @After("pointcut()")
        public void after(JoinPoint point) {
            //清理掉当前设置的数据源,让默认的数据源不受影响
            HandlerDataSource.clear();
        }
    
    }
    
    • 配置数据源在Spring 核心容器中配置
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis
    jdbc.username=root
    jdbc.password=root
    jdbc2.url=jdbc:mysql://127.0.0.1:3306/datasource2
    
    <!-- 配置数据源 -->
        <bean id="dataSource0" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init">
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
            <property name="maxActive" value="10"/>
        </bean>
        <bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init">
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc2.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
            <property name="maxActive" value="10"/>
        </bean>
    • 配置之前我们实现的数据源选择的中间层AbstractRoutingDataSource的实现类,这里的key就是数据源信息的别名,通过这个key可以选择到数据源的信息。MultipleDataSourceToChoose就是上面写的数据源选择器的实现类
    bean id="dataSource" class="com.common.utils.manydatasource.MultipleDataSourceToChoose" lazy-init="true">
            <description>数据源</description>
            <property name="targetDataSources">
                <map key-type="java.lang.String" value-type="javax.sql.DataSource">
                    <entry key="datasource0" value-ref="dataSource0" />
                    <entry key="datasource1" value-ref="dataSource1" />
                </map>
            </property>
            <!-- 设置默认的目标数据源 -->
            <property name="defaultTargetDataSource" ref="dataSource0" />
        </bean>
    • SessionFactory的配置还是照旧,使用以前的配置,只不过当前选择的数据源是datasource,也就是数据源选择的中间层MultipleDataSourceToChoose,因为当前的中间层中实现了DataSource这个接口,所以可以看做为DataSource的是实现类啦,所以配置不会出现问题。
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!--指定Hibernate属性 -->
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.show_sql">false</prop>
                    <prop key="hibernate.format_sql">false</prop>
                    <prop key="hibernate.hbm2ddl.auto">update</prop>
                    <prop key="hibernate.autoReconnect">true</prop>
                    <prop key="hibernate.jdbc.batch_size">50</prop>
                  <prop key="hibernate.connection.autocommit">false</prop>
                  <prop key="hibernate.connection.release_mode">after_transaction</prop>
                  <prop key="hibernate.bytecode.use_reflection_optimizer">false</prop>
                </props>
            </property>
            <property name="packagesToScan">
                <list>
                    <value>com.module</value>
                </list>
            </property>
        </bean>
    • 简单的使用AOP进行测试一下,这里测试的结果时不同的,所以是生效的,使用了不同的数据源,但是底层的实现没有进行任何的修改处理。
    @Service
    @Slf4j
    public class UserInfoService implements IUserInfoService {
    
    
        @Resource
        private UserDao userDao;
        @Autowired
        private CommonHibernateDao commonDao;
    
        @TestValidateParam
        public User getUserInfoById(Integer id) {
            return userDao.findById(id);
        }
    
        @DynamicSwitchDataSource(dataSource = "datasource0")
        public void save(User user) {
            userDao.save(user);
        }
    
        @DynamicSwitchDataSource(dataSource = "datasource1")
        public List<User> findAll(){
            String sql = "select u.userName as name,u.userAge as age,u.userAddress as address,u.id from user u";
            List<User> list =commonDao.findListBySQL(sql,User.class);
            return list;
        }
    
    }
    • 也可以不适用AOP,直接在编程中实现,通过测试,结果分别为两个数据库中的信息
      public void test(){
            HandlerDataSource.putDataSource("datasource1");
            String sql = "select u.userName as name,u.userAge as age,u.userAddress as address,u.id from user u";
            List<User> list =commonDao.findListBySQL(sql,User.class);
    
            HandlerDataSource.putDataSource("datasource0");
            commonDao.deleteById("2",User.class);
        }
    
    • 实现原理,MultipleDataSourceToChoose的继承结构图,之前说过他是DataSource的子类,由于无论我们是使用Mybatis还是使用Hibernate进行SQL操作的时候总会执行getConnection(),无论我们的数据源是否使用了数据库连接池,因为数据库连接池的主要作用就是保持一堆的Connection不进行关闭的处理,节省我们的关闭和打开连接的开销。http://blog.csdn.net/shuaihj/article/details/14223015/ 浅谈数据库连接池说的简单易懂。 Connection getConnection() throws SQLException;所以这句话总是要执行的,只是AbstractRoutingDataSource这个类给我们进行了一些中介的处理,在获取Connection的时候会去寻找保存的DataSource的引用,到底是选择哪个DataSource进行处理,看代码!

    这里写图片描述
    - 配置的参数

     <bean id="dataSource" class="com.common.utils.manydatasource.MultipleDataSourceToChoose" lazy-init="true">
            <description>数据源</description>
            <property name="targetDataSources">
                <map key-type="java.lang.String" value-type="javax.sql.DataSource">
                    <entry key="datasource0" value-ref="dataSource0" />
                    <entry key="datasource1" value-ref="dataSource1" />
                </map>
            </property>
            <!-- 设置默认的目标数据源 -->
            <property name="defaultTargetDataSource" ref="dataSource0" />
        </bean>

    targetDataSources,是一个Map对于数据源的引用

    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
            this.targetDataSources = targetDataSources;
        }

    对于实现SQL的Connection getConnection() throws SQLException的实现,其实就是代理模式找到之前Map的引用,通过key,而这个key就是我们灵活配置的key,通过这个key就可以寻找到这个值。

        public Connection getConnection() throws SQLException {
            return determineTargetDataSource().getConnection();
        }

    这里说的非常的详细,通过钩子函数让子类去实现,寻找特定的key,然后选择DataSource 的时候就可以很灵活的使用啦!

    /**
         * Retrieve the current target DataSource. Determines the
         * {@link #determineCurrentLookupKey() current lookup key}, performs
         * a lookup in the {@link #setTargetDataSources targetDataSources} map,
         * falls back to the specified
         * {@link #setDefaultTargetDataSource default target DataSource} if necessary.
         * @see #determineCurrentLookupKey()
         */
        protected DataSource determineTargetDataSource() {
            Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
            Object lookupKey = determineCurrentLookupKey();
            DataSource dataSource = this.resolvedDataSources.get(lookupKey);
            if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
                dataSource = this.resolvedDefaultDataSource;
            }
            if (dataSource == null) {
                throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
            }
            return dataSource;
        }

    这个就是模板方法模式中常见的钩子函数,在HttpServlet中也有类似的使用钩子,非常的棒,不过这个是必须实现,httpServlet不是必须实现,只是添加一些补充。由于每次执行数据库的调用,总会执行这个getConnection方法,每次都查看AOP中是否设置了当前的数据源,然后找到Map的引用的代理的数据源的Connection方法,原理没有变化的。

    /**
         * Determine the current lookup key. This will typically be
         * implemented to check a thread-bound transaction context.
         * <p>Allows for arbitrary keys. The returned key needs
         * to match the stored lookup key type, as resolved by the
         * {@link #resolveSpecifiedLookupKey} method.
         */
        protected abstract Object determineCurrentLookupKey();

    这里就是我们的实现的数据源的选择哦!

    /**
     * descrption: 多数据源的选择
     * authohr: wangji
     * date: 2017-08-21 10:32
     */
    public class MultipleDataSourceToChoose extends AbstractRoutingDataSource {
    
        /**
         * @desction: 根据Key获取数据源的信息,上层抽象函数的钩子
         * @author: wangji
         * @date: 2017/8/21
         * @param:
         * @return:
         */
        @Override
        protected Object determineCurrentLookupKey() {
            return HandlerDataSource.getDataSource();
        }
    }
    展开全文
  • 数据源切换

    2019-03-08 09:57:53
    数据源切换,用在JAVA中链接不同数据库的切换,大大减少开发的耦合度,方便开发
  • 今天小编就为大家分享一篇关于Spring配置多数据源切换,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • 最近在配置spring框架时...无事务数据源切换 见 http://blog.csdn.net/shadowsick/article/details/8878448  主要说一下带事务数据源切换的配置 首先在spring-database.xml中配置两个数据源(默认数据源),和应用的切面

    最近在配置spring框架时需要使用多数据源,其中遇到一些问题,特此记录

    无事务数据源切换

    见 http://blog.csdn.net/shadowsick/article/details/8878448 

    主要说一下带事务数据源切换的配置

    首先在spring-database.xml中配置两个数据源(默认数据源),和应用的切面

    <!-- backend数据源  --> 
        <bean id="dataSourceBackend" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >  
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>  
            <property name="url" value="${backend.url}"></property>  
            <property name="username" value="${backend.username}"></property>  
            <property name="password" value="${backend.password}"></property>  
        </bean>
        <!-- pi数据源  --> 
        <bean id="dataSourcePIDB" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >  
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>  
            <property name="url" value="${pi.url}"></property>  
            <property name="username" value="${pi.username}"></property>  
            <property name="password" value="${pi.password}"></property>  
        </bean>  
        
        <!-- 多数据源配置  --> 
        <bean id="dynamicDataSource" class="com.test.base.dao.DynamicDataSource">  
            <property name="targetDataSources">  
                <map key-type="java.lang.String">  
                    <entry value-ref="dataSourceBackend" key="dataSourceBackend"></entry>  
                	<entry value-ref="dataSourcePIDB" key="dataSourcePIDB"></entry>
                </map>  
            </property>  <!--默认数据源-->
            <property name="defaultTargetDataSource" ref="dataSourceBackend">  
            </property>  
        </bean>
        <aop:config><!--切面-->  
            <aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
                <aop:pointcut id="daoBackend" expression="execution(* com.test.backend.service.ReportService.*(..))" />
                <aop:before pointcut-ref="daoBackend" method="setdataSourceBackend" />
                
                <aop:pointcut id="daopidb" expression="execution(* com.test.backend.service.MacIpService.*(..))" />
                <aop:before pointcut-ref="daopidb" method="setdataSourcePIDB" /><!--切面before调用的方法-->
                <aop:after pointcut-ref="daopidb" method="cleardataSource" /><!--切面after调用的方法-->
    
            </aop:aspect>  
        </aop:config> 

    多数据源route类

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


    数据源内容配置类

    public class DatabaseContextHolder {
    	
    	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
    	  
        public static void setCustomerType(String customerType) {
            contextHolder.set(customerType);  
        }  
      
        public static String getCustomerType() {  
            return contextHolder.get();  
        }  
      
        public static void clearCustomerType() {  
            contextHolder.remove();  
        } 
    	
    }
    @Component
    public class DataSourceInterceptor {
    	
        public void setdataSourceBackend(JoinPoint jp) {  
            DatabaseContextHolder.setCustomerType("dataSourceBackend");  
        }
        public void setdataSourcePIDB(JoinPoint jp) {  
        	DatabaseContextHolder.setCustomerType("dataSourcePIDB");  
        }
        
        public void cleardataSource(JoinPoint jp){
        	DatabaseContextHolder.clearCustomerType();
        }
        
    }

    上边xml中配置的 dataSourceInterceptor 会找到class DataSourceInterceptor,并执行set**方法和clear**方法,并设置CustomerType(数据源)给contextHolder,以便于

    在determineCurrentLookupKey方法中可以获得到改变后的数据源.


    然后是事务的配置

    在spring-application.xml中

    加上如下配置

    </pre></p><p><pre name="code" class="html"> <import resource="spring-database.xml"/>
     
        
        
        <!-- 配置SessionFactory -->  
        <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">  
            <property name="dataSource" ref="dynamicDataSource" />  
            <property name="hibernateProperties">  
                <props>  
                	<prop key="hibernate.autoReconnect">true</prop>
                	<prop key="hibernate.connection.autocommit">true</prop>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>  
                    <prop key="hibernate.show_sql">true</prop>  
                </props>  
            </property>  
            <property name="packagesToScan" value="com.test.backend.entity" />
        </bean>  
        
        <!-- 配置一个事务管理器 -->  
        <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">  
            <property name="sessionFactory" ref="sessionFactory"/>  
        </bean>  
        
        <!-- 事务管理  start-->
        <tx:annotation-driven transaction-manager="transactionManager" />
        
        <tx:advice id="txAdvice" transaction-manager="transactionManager">  
            <tx:attributes>  
                <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> 
                <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> 
                <tx:method name="del*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> 
                <tx:method name="find*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> 
                <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            </tx:attributes>  
        </tx:advice>  
        
    	<aop:config>  
            <!-- 实施事务 -->  
            <aop:pointcut id="txPointcut" expression="execution(* com.test.backend.service.*.*(..))" />  
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />  
        </aop:config>
        <!-- 事务管理  end-->

    在web.xml中加上了spring管理hibernate的session

    <filter>
    		<filter-name>openSessionInView</filter-name>
    		<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
    		<init-param>
    			<param-name>sessionFactoryBeanName</param-name>
    			<param-value>sessionFactory</param-value>
    		</init-param>
    	</filter>
    
    	<filter-mapping>
    		<filter-name>openSessionInView</filter-name> 
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>



    注意:加上事务时候,我这的切面切的是service层,当切换数据源时,一定要把所有的方法加到你要实施事务的attributes中,或者写成*(任何方法都实施事务)也可以.否则不能切换数据源,详情可见http://blog.csdn.net/wangpeng047/article/details/43450189的博客

    这里一句话带过,事务和数据源是绑定的,当你在service层加上事务,你想在dao层切换数据源是不可以的,由于在进入该层之前事务已经通过拦截器开启,因此在该层切换数据源也是不行的.

    以上是我本地配置的带事务管理的数据源切换实例,如有问题感谢大家提出指正互相学习.



    展开全文
  • 本文介绍两种切换数据库的方法。 方法1:数据库信息都配置在spring xml中,适用于一般数据库切换。...方法2:将数据库信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。...
  • 该工程使用spring boot 和 Mybatis 实现多数据源,动态数据源切换。以及在过程遇到Spring事务执行顺序与数据源切换执行顺序设置 数据源动态切换由conf/dal 包下4个类实现; DynamicDataSour...
  • springboot+mybatis+mysql+AbstractRoutingDataSource实现多数据源切换(一写多读数据源)项目中包含完整的demo案例配备文档说明,亲测可用;实现简单,代码思路清晰.
  • 主要介绍了SpringBoot+Mybatis实现动态数据源切换方案过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • spring boot mybatis plus 多数据源切换的demo解压运行
  • 主要介绍了使用Spring的AbstractRoutingDataSource实现多数据源切换示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
  • 本篇文章主要介绍了详解Spring(AbstractRoutingDataSource)实现动态数据源切换,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
  • SpringBoot使用AOP完成多数据源切换,下载后导入即可使用,JDK1.8
  • SpringBoot整合Sharding-JDBC,实现从数据库读取sharding-jdbc数据源,实现多种数据库数据源切换,数据库方言动态切换
  • 本篇文章主要介绍了Java注解实现动态数据源切换的实例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 本篇文章主要介绍了SpringBoot AOP方式实现多数据源切换的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了Spring与Mybatis相结合实现多数据源切换功能的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
  • cobar+spring+oracle数据源切换
  • 在项目中遇到需要使用多数据源的情况,解决办法是,使用注解,切面...问题:事务开启一般是在service中开启,事务开启后会导致数据源切换失败,数据源切换需要在事务开启前执行。解决:数据源切入点@Pointcut增加ser...
  • 整理的分布式事务 及动态数据源切换 基于数据库XA协议 maven 包可直接使用 maven 引入路径: <groupId>com.maple</groupId> <artifactId>DCSDataSourcesServer <version>0.0.1-SNAPSHOT 事务及数据源标签...
  • springboot多数据源切换mysql+sql server,该事例代码demo,下载下来就能使用,使用工具idea,自行安装mysql和sql server
  • spring + druid 配置动态配置数据源以及多数据源切换功能实现 数据源连接池使用druid 其他的数据源基本原理相同 spring中配置默认数据源连接池如下: <!-- 数据源配置, 使用 BoneCP 数据库连接池 --> &...
  • 项目实例 带缓存框架 数据源切换 测试通过
  • Spring Boot MyBatis 动态数据源切换、多数据源,读写分离https://blog.csdn.net/u013360850/article/details/78861442项目地址 https://github.com/helloworlde/SpringBoot-DynamicDataSource 本项目使用 Spring ...
  • 1.配置数据源,2.动态数据源切换,3.注册动态数据源,4.动态数据源注解切点,5.通过切点切换数据源,6.在service层通过注解切换不同数据源,7.启动项目
  • SpringBoot 多数据源切换

    千次阅读 2019-06-29 20:40:01
    SpringBoot多数据源切换 目的: 1)读写库在代码端实现 2)需要操作多数据库的时候需要使用 核心类 核心类AbstractRoutingDataSource,这个类是一个抽象化的DataSource类。可以用来选择多个数据源,确认数据源。 ...
  • 使用注解配置实现Spring动态数据源切换,实现原理 1、自定义动态数据源类DynamicDataSource: 实现spring类AbstractRoutingDataSource的方法determineCurrentLookupKey 2、自定义Spring AOP类DataSourceAspect 3、...
  • SpringBoot2.x 继承 AbstractRoutingDataSource 动态数据源切换实现 JPA读写分离。 使用MyCat代理MySQL8数据库,添加root账户(读写)和user账户(只读)模拟读写简单分离。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 297,386
精华内容 118,954
关键字:

数据源切换