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

原文地址:https://blog.csdn.net/tengxing007/article/details/78424645

前言

在开发过程中可能需要用到多个数据源,比如一个项目(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
#db1
spring.datasource.db1.url=jdbc:mysql://127.0.0.1:3306/api_resources?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
spring.datasource.db1.username=api
spring.datasource.db1.password=api
spring.datasource.db1.driver-class-name=com.mysql.jdbc.Driver
#db2
spring.datasource.db2.url=jdbc:mysql://127.0.0.1:3306/ssm?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
spring.datasource.db2.username=api
spring.datasource.db2.password=api
spring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver

配置dataSource

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

/**
 * Author: Starry.Teng
 * Email: [email protected]
 * Date: 17-11-1
 * Time: 下午9:14
 * Describe: DataSource Config
 */
@Configuration
public 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: [email protected]
 * 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: [email protected]
 * 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: [email protected]
 * Date: 17-11-1
 * Time: 下午9:14
 * Describe: DataSource Config
 */
@Configuration
public 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: [email protected]
 * 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
应用场景 解耦比较大的项目,模块化开发 主从分布,读写分离

知识点

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

猜你喜欢

转载自blog.csdn.net/u014172271/article/details/80202451
今日推荐