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

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

下面看看图片

css


单数据源的场景(通常的Web项目工程这样配置进行处理,就已经比较可以知足咱们的业务需求)

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

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

mysql

示例:spring

1.加个依赖sql

        <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.数据源配置类springboot

复制代码
/** * 主数据源 */ @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.数据源上下文mybatis

复制代码
/** * 数据源上下文 */ 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路由类app

复制代码
/* * 重写的函数决定了最后选择的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

相关文章
相关标签/搜索