springBoot 动态数据源以及Mybatis多数据源

来源:互联网 发布:windows小键盘 编辑:程序博客网 时间:2024/06/06 18:36

前言

在开发过程中可能需要用到多个数据源,比如一个项目(MySQL)就是和(SQL Server)混合使用,就需要使用多数据源;如果业务场景比较复炸,可以使用动态数据源,灵活切换,典型的应用就是读写分离。下面分两个模块来配置数据源,大家可以根据自己实际情况配置。

多数据源

禁用DataSourceAutoConfiguration

如果DataSourceAutoConfiguration不禁用的话,就会报错,多个数据源,无法装配哪一个。在springBoot的主程序入口的注解

@SpringBootApplication(exclude = {        DataSourceAutoConfiguration.class})

配置application.properties

#datasource 这是自动装配的默认配置禁止,这里不能使用#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource#spring.datasource.driver-class-name=com.mysql.jdbc.Driver#spring.datasource.url=jdbc:mysql://127.0.0.1:3306/api_resources?#autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true#spring.datasource.username=api#spring.datasource.password=api#db1spring.datasource.db1.url=jdbc:mysql://127.0.0.1:3306/api_resources?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=truespring.datasource.db1.username=apispring.datasource.db1.password=apispring.datasource.db1.driver-class-name=com.mysql.jdbc.Driver#db2spring.datasource.db2.url=jdbc:mysql://127.0.0.1:3306/ssm?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=truespring.datasource.db2.username=apispring.datasource.db2.password=apispring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver

配置dataSource

配置两个数据源,分别为db1,和db2

/** * Author: Starry.Teng * Email: tengxing7452@163.com * Date: 17-11-1 * Time: 下午9:14 * Describe: DataSource Config */@Configurationpublic class DataSourceConfig {    @Autowired    Environment env;    @Bean(name = "ds1")    public DataSource dataSource1() {        DruidDataSource dataSource = new DruidDataSource();        dataSource.setUrl(env.getProperty("spring.datasource.db1.url"));        dataSource.setUsername(env.getProperty("spring.datasource.db1.username"));        dataSource.setPassword(env.getProperty("spring.datasource.db1.password"));        dataSource.setDriverClassName(env.getProperty("spring.datasource.db1.driver-class-name"));        return dataSource;    }    @Bean(name = "ds2")    public DataSource dataSource2() {        DruidDataSource dataSource = new DruidDataSource();        dataSource.setUrl(env.getProperty("spring.datasource.db2.url"));        dataSource.setUsername(env.getProperty("spring.datasource.db2.username"));        dataSource.setPassword(env.getProperty("spring.datasource.db2.password"));        dataSource.setDriverClassName(env.getProperty("spring.datasource.db2.driver-class-name"));        return dataSource;    }}

Mybatis配置

为两个数据源分贝配置一个MybatisDb1Config类和MybatisDb2Config类,对数据源进行管理,注意他们所管理的包是不同的。

/** * Author: Starry.Teng * Email: tengxing7452@163.com * Date: 17-11-1 * Time: 下午9:15 * Describe: MybatisDb1 Config */@Configuration@MapperScan(basePackages = {"cn.yjxxclub.demo.datasource.dao.db1"}, sqlSessionFactoryRef = "sqlSessionFactory1")public class MybatisDb1Config {    @Qualifier("ds1")    @Autowired    DataSource dataSource;    @Bean(name = "sqlSessionFactory1")    public SqlSessionFactory sqlSessionFactory1() throws Exception {        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();        factoryBean.setDataSource(dataSource);        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();        factoryBean.setMapperLocations(resolver.getResources("classpath:mappers/db1/*.xml"));        factoryBean.setTypeAliasesPackage("cn.yjxxclub.demo.datasource.model");        return factoryBean.getObject();    }    @Bean    public SqlSessionTemplate sqlSessionTemplate1() throws Exception {        SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory1()); // 使用上面配置的Factory        return template;    }}
/** * Author: Starry.Teng * Email: tengxing7452@163.com * Date: 17-11-1 * Time: 下午9:26 * Describe: MybatisDb2 Config */@Configuration@MapperScan(basePackages = {"cn.yjxxclub.demo.datasource.dao.db2"}, sqlSessionFactoryRef = "sqlSessionFactory2")public class MybatisDb2Config {    @Qualifier("ds2")    @Autowired    DataSource dataSource;    @Bean(name = "sqlSessionFactory2")    public SqlSessionFactory sqlSessionFactory2() throws Exception {        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();        factoryBean.setDataSource(dataSource);        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();        factoryBean.setMapperLocations(resolver.getResources("classpath:mappers/db2/*.xml"));        factoryBean.setTypeAliasesPackage("cn.yjxxclub.demo.datasource.model");        return factoryBean.getObject();    }    @Bean    public SqlSessionTemplate sqlSessionTemplate2() throws Exception {        SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory2());        return template;    }}

编写Dao和Mapper

Mapper.java和mapper.xml在basePackages和classpath对应放置即可,和springboot整合Mybtis一样,这里就不说了,然后正常启动即可。代码在此:https://github.com/tengxing/Multiple-dataSources

数据源动态切换

数据源注册

/** * Author: Starry.Teng * Email: tengxing7452@163.com * Date: 17-11-1 * Time: 下午9:14 * Describe: DataSource Config */@Configurationpublic class DataSourceConfig {    @Autowired    Environment env;    @Bean(name = "ds1")    public DataSource dataSource1() {        DruidDataSource dataSource = new DruidDataSource();        dataSource.setUrl(env.getProperty("spring.datasource.db1.url"));        dataSource.setUsername(env.getProperty("spring.datasource.db1.username"));        dataSource.setPassword(env.getProperty("spring.datasource.db1.password"));        dataSource.setDriverClassName(env.getProperty("spring.datasource.db1.driver-class-name"));        return dataSource;    }    @Bean(name = "ds2")    public DataSource dataSource2() {        DruidDataSource dataSource = new DruidDataSource();        dataSource.setUrl(env.getProperty("spring.datasource.db2.url"));        dataSource.setUsername(env.getProperty("spring.datasource.db2.username"));        dataSource.setPassword(env.getProperty("spring.datasource.db2.password"));        dataSource.setDriverClassName(env.getProperty("spring.datasource.db2.driver-class-name"));        return dataSource;    }    @Bean(name = "dynamicDS1")//注意这个bean是mybatis的sqlSessionFacatory所管理的dataSource    public DataSource dataSource() {        DynamicDataSource dynamicDataSource = new DynamicDataSource();        // 默认数据源        dynamicDataSource.setDefaultTargetDataSource(dataSource1());        // 配置多数据源        Map<Object, Object> dsMap = new HashMap(5);        dsMap.put("ds1", dataSource1());        dsMap.put("ds2", dataSource2());        dynamicDataSource.setTargetDataSources(dsMap);        return dynamicDataSource;    }}

动态数据源编写

需要继承AbstractRoutingDataSource类

/** * Author: http://blog.csdn.net/neosmith/article/details/61202084 * Date: 17-11-2 * Time: 下午2:56 * Describe: 动态数据源 */public class DynamicDataSource extends AbstractRoutingDataSource {    private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);    @Override    protected Object determineCurrentLookupKey() {        log.debug("数据源为{}", DataSourceContextHolder.getDB());        return DataSourceContextHolder.getDB();    }}

编写DataSourceContextHolder

/** * Author: http://blog.csdn.net/neosmith/article/details/61202084 * Date: 17-11-2 * Time: 下午2:56 * Describe: DataSource ContextHolder */public class DataSourceContextHolder {    public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);    /**     * 默认数据源     */    public static final String DEFAULT_DS = "ds1";    /**     * 获取当前线程     */    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();    // 设置数据源名    public static void setDB(String dbType) {        log.debug("切换到{}数据源", dbType);        contextHolder.set(dbType);    }    // 获取数据源名    public static String getDB() {        return (contextHolder.get());    }    // 清除数据源名    public static void clearDB() {        contextHolder.remove();    }}

咋们到时候只要调用setDB()方法即可实现数据源的切换,下面使用Aop的方式进行动态切换。

编写代理类

/** * Author: http://blog.csdn.net/neosmith/article/details/61202084 * Date: 17-11-2 * Time: 下午3:01 * Describe: 动态数据源代理类 * 逻辑:对方法@DB注解的方法进行切面换数据源操作 */@Aspect@Component@Order(value=-1) //保证该AOP在@Transactional之前执行public class DynamicDataSourceAspect {    @Before("@annotation(DB)")    public void beforeSwitchDS(JoinPoint point){        //获得当前访问的class        Class<?> className = point.getTarget().getClass();        //获得访问的方法名        String methodName = point.getSignature().getName();        //得到方法的参数的类型        Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();        String dataSource = DataSourceContextHolder.DEFAULT_DS;        try {            // 得到访问的方法对象            Method method = className.getMethod(methodName, argClass);            // 判断是否存在@DS注解            if (method.isAnnotationPresent(DB.class)) {                DB annotation = method.getAnnotation(DB.class);                // 取出注解中的数据源名                dataSource = annotation.value();            }        } catch (Exception e) {            e.printStackTrace();        }        // 切换数据源        DataSourceContextHolder.setDB(dataSource);    }    @After("@annotation(DB)")    public void afterSwitchDS(JoinPoint point){        DataSourceContextHolder.clearDB();    }}

自定义注解

/** * Author: Starry.Teng * Email: tengxing7452@163.com * Date: 17-11-2 * Time: 下午3:00 * Describe: */@Retention(RetentionPolicy.RUNTIME)@Target({        ElementType.METHOD})public @interface DB {    String value() default "ds1";}

serviceImpl

    @DB("ds1")    public Object getDs1(){        Singer singer = singerMapper.findByName("陈奕迅");        logger.info("/n"+singer);        return null;    }    @DB("ds2")    public Object getDs2(){        Book book = bookMapper.findById(bookMapper.list().get(0).getId());        logger.info(book+"/n");        return null;    }

最后调用吧!!!项目在此:https://github.com/tengxing/DynamicDataSource

两者比较

类别 多数据源 动态数据源 作用 配置多个数据源,分模块开发 通过灵活的手段对数据库进行灵活切库 原理 实例化多个SsqlSessionFactory Spring+AOP 作用框架 Mybatis Spring 应用场景 解耦比较大的项目,模块化开发 主从分布,读写分离

知识点

上面的两种方式都达到了”换数据源”目的,只是实现的原理和方式不一样而已,都是优虐,还是那句话,没有最好的方法,只有在特定的环境下最适合的解决方案。

参考文章

http://blog.csdn.net/neosmith/article/details/61202084
http://www.cnblogs.com/hdwang/p/7041096.html
http://blog.csdn.net/catoop/article/details/50575038
https://github.com/zeq9069/koala/tree/master/src/main/java/org/kyrin/koala