• 如何在spring框架中解决多数据源的问题


    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拦截特定的注解,设置当前的数据源信息,也可以手动的设置当前的数据源,在编程的类中。

    示例:

    1.加个依赖

            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
           <version>1.3.1</version> </dependency>

    2.application.properties配置文件

    复制代码
    #主从数据库
    master.db.driverClassName=com.mysql.jdbc.Driver
    master.db.url=jdbc:mysql://localhost:3306/cbd?characterEncoding=UTF-8&useUnicode=true&useSSL=false
    master.db.username=root
    master.db.password=admin
    slave.db.driverClassName=com.mysql.jdbc.Driver
    slave.db.url=jdbc:mysql://localhost:3306/cbd_test?characterEncoding=UTF-8&useUnicode=true&useSSL=false
    slave.db.username=root
    slave.db.password=admin
    
    mybatis.config-location= classpath:config/mybatis-config.xml
    mybatis.mapper-locations=classpath:mapper/**/*.xml
    复制代码

    3.禁用springboot默认加载数据源配置

    复制代码
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
    public class Application {
        
        public static void main(String[] args) throws Exception {
            SpringApplication.run(Application.class, args);
        }
    }
    复制代码

    4.数据源配置类

    复制代码
    /**
     * 主数据源
     */
    @Configuration
    @ConfigurationProperties(prefix = "master.db")
    public class MasterDataSourceConfig {
        private String url;
        private String username;
        private String password;
        private String driverClassName;
    }
    复制代码
    复制代码
    /**
     * 从数据源配置
     */
    @Configuration
    @ConfigurationProperties(prefix = "slave.db")
    public class SlaveDataSourceConfig {
        private String url;
        private String username;
        private String password;
        private String driverClassName;
    }
    复制代码
    复制代码
    /**
     * 数据源配置类
     */
    @Configuration
    public class DataSourceComponent {
    
        @Resource
        private MasterDataSourceConfig masterDataSourceConfig;
    
        @Resource
        private SlaveDataSourceConfig slaveDataSourceConfig;
    
    
         @Bean(name = "master")
        public DataSource masterDataSource() {
            DataSource dataSource = new DataSource();
            dataSource.setUrl(masterDataSourceConfig.getUrl());
            dataSource.setUsername(masterDataSourceConfig.getUsername());
            dataSource.setPassword(masterDataSourceConfig.getPassword());
            dataSource.setDriverClassName(masterDataSourceConfig.getDriverClassName());
            return dataSource;
        }
    
        @Bean(name = "slave")
        public DataSource slaveDataSource() {
            DataSource dataSource = new DataSource();
            dataSource.setUrl(slaveDataSourceConfig.getUrl());
            dataSource.setUsername(slaveDataSourceConfig.getUsername());
            dataSource.setPassword(slaveDataSourceConfig.getPassword());
            dataSource.setDriverClassName(slaveDataSourceConfig.getDriverClassName());
            return dataSource;
        }
    
        @Primary//不加这个会报错。
        @Bean(name = "multiDataSource")
        public MultiRouteDataSource exampleRouteDataSource() {
            MultiRouteDataSource multiDataSource = new MultiRouteDataSource();
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put("master", masterDataSource());
            targetDataSources.put("slave", slaveDataSource());
            multiDataSource.setTargetDataSources(targetDataSources);
            multiDataSource.setDefaultTargetDataSource(masterDataSource());
            return multiDataSource;
        }
    /**
         * 根据数据源创建SqlSessionFactory
         */
        @Bean
        public SqlSessionFactory sqlSessionFactory(@Qualifier("ngDataSource") DataSource ngDataSource,
                @Qualifier("tzDataSource") DataSource tzDataSource, @Qualifier("ghDataSource") DataSource ghDataSource)
                throws Exception {
            SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
            fb.setDataSource(this.dataSource());// 指定数据源(这个必须有,否则报错)
            // 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
            fb.setTypeAliasesPackage("com.dxz.multidatasources.mapper");//指定基包
            // String[] mybatisMapperLocations = {"classpath*:/orm/*.xml", "classpath*:/orm/**/*.xml"};
            fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/orm/*.xml"));//

            return fb.getObject();
        }

        /**
         * 配置事务管理器
         */
        @Bean
        public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
            return new DataSourceTransactionManager(dataSource);
        }

        /**
         * 注册ServletRegistrationBean
         *
         * @return
         */
        @Bean
        public ServletRegistrationBean druidServlet() {
            ServletRegistrationBean reg = new ServletRegistrationBean();
            reg.setServlet(new StatViewServlet());
            reg.addUrlMappings("/druid/*");
            reg.addInitParameter("allow", ""); // 白名单 return reg;
            reg.addInitParameter("loginUsername", "duan");
            reg.addInitParameter("loginPassword", "123456");
            reg.addInitParameter("resetEnable", "false");
            return reg;
        }

        /**
         * 注册FilterRegistrationBean
         *
         * @return
         */
        @Bean
        public FilterRegistrationBean filterRegistrationBean() {
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
            filterRegistrationBean.setFilter(new WebStatFilter());
            filterRegistrationBean.addUrlPatterns("/*");
            filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
            filterRegistrationBean.addInitParameter("profileEnable", "true");
            filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
            filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
            filterRegistrationBean.addInitParameter("DruidWebStatFilter", "/*");
            return filterRegistrationBean;
        } }
    复制代码

    5.数据源上下文

    复制代码
    /**
     * 数据源上下文
     */
    public class DataSourceContext {
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    
        public static void setDataSource(String value) {
            contextHolder.set(value);
        }
    
        public static String getDataSource() {
            return contextHolder.get();
        }
    
        public static void clearDataSource() {
            contextHolder.remove();
        }
    }
    复制代码

    6.DataSource路由类

    复制代码
    /*
    *    重写的函数决定了最后选择的DataSource
    */
    public class MultiRouteDataSource extends AbstractRoutingDataSource {
    
        @Override
        protected Object determineCurrentLookupKey() {
            //通过绑定线程的数据源上下文实现多数据源的动态切换,有兴趣的可以去查阅资料或源码
            return DataSourceContext.getDataSource();
        }
    
    }
    复制代码

    7.使用,修改上下文中的数据源就可以切换自己想要使用的数据源了。

    复制代码
        public UserVO findUser(String username) {
            DataSourceContext.setDataSource("slave");
            UserVO userVO = userMapper.findByVO(username);
            System.out.println(userVO.getName()+"=====================");
            return null;
        }
    复制代码

    这种是在业务中使用代码设置数据源的方式,也可以使用AOP+注解的方式实现控制,方法多多!

    问题

    有的springboot项目启动时报错:

    ***************************
    APPLICATION FAILED TO START
    ***************************
    
    Description:
    
    Cannot determine embedded database url for database type NONE
    
    Action:
    
    If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).

    解决办法:

    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

    参考:

    https://blog.csdn.net/hengyunabc/article/details/78762097

  • 相关阅读:
    2013-2014 NBA 东西部决赛 + 总决赛合集
    小萌库
    小萌库一周电影大合集
    小萌库
    小萌库- 新海诚那些唯美感人的动漫
    小萌库 一周漫画精彩回顾
    小萌库
    Week10-数据库
    Week9-RabbitMQ、Redis、Mysql
    Week8-python(线程、进程、协程)
  • 原文地址:https://www.cnblogs.com/duanxz/p/4516388.html
Copyright © 2020-2023  润新知