业务系统复杂程度增加, 为了解决数据库 I/O 瓶颈, 很自然会进行拆库拆表分服务来应对. 这就会出现一个系统中可能会访问多处数据库, 需要配置多个数据源.
第一种场景: 项目服务从其它多处数据库取基础数据进行业务处理, 因此各库之间不会出现重表等情况.
第二种场景: 为了减轻写入压力进行读写分库, 读走从库, 写为主库. 此种表名等信息皆为一致.
第三种场景: 以上两种皆有. 对于某些业务需要大数据量的汇总统计, 希望不影响正常业务必须走从库 (表信息一致), 某些配置信息不存在读写压力, 出现不分库 (表信息不一致)
项目源代码:
https://github.com/zzsong/springboot-multiple-datasource.git
有三个目录:
one:
直接使用多 @Bean 配置,@MapperScan 来路径区分读何库
two:
使用注解的方式来标识走何 dataSource,AOP 拦截注入动态数据源
third:
使用 spring 的 Bean 命名策略进行区分数据来源
项目技术选型: springBoot2.2.5 + mybatis + druid + MySQL
先看主要的 pom 包
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.2.5.RELEASE</version>
- <relativePath/>
- </parent>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-jdbc</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-jdbc</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
- <dependency>
- <groupId>org.mybatis.spring.boot</groupId>
- <artifactId>mybatis-spring-boot-starter</artifactId>
- <version>2.1.2</version>
- </dependency>
- <dependency>
- <groupId>MySQL</groupId>
- <artifactId>MySQL-connector-java</artifactId>
- <version>8.0.19</version>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid-spring-boot-starter</artifactId>
- <version>1.1.21</version>
- </dependency>
application.YAML
- spring:
- datasource:
- druid:
- core:
- url: jdbc:MySQL:///kc_core?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
- username: root
- password: 123456
- driver-class-name: com.MySQL.cj.jdbc.Driver
- type: com.alibaba.druid.pool.DruidDataSource
- schedule:
- url: jdbc:MySQL:///kc_schedule?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
- username: root
- password: 123456
- driver-class-name: com.MySQL.cj.jdbc.Driver
- type: com.alibaba.druid.pool.DruidDataSource
MySQL 新版本必须带有 serverTimezone, 不然会报连接异常.
第一种: 通过 @MapperScans 扫描匹配相关的数据源
- @Configuration
- @MapperScans({
- @MapperScan(basePackages = "com.zss.one.mapper.core", sqlSessionTemplateRef = "coreSqlSessionTemplate",sqlSessionFactoryRef = "coreSqlSessionFactory"),
- @MapperScan(basePackages = "com.zss.one.mapper.schedule", sqlSessionTemplateRef = "scheduleSqlSessionTemplate",sqlSessionFactoryRef = "scheduleSqlSessionFactory")
- })
- public class MybatisOneConfig {
- @Bean
- @ConfigurationProperties(prefix = "spring.datasource.druid.core")
- public DataSource coreDataSource(){
- return DruidDataSourceBuilder.create().build();
- }
- @Bean
- public SqlSessionFactory coreSqlSessionFactory(@Qualifier("coreDataSource") DataSource coreDataSource) throws Exception {
- SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
- sessionFactory.setDataSource(coreDataSource);
- sessionFactory.getObject().getConfiguration().setJdbcTypeForNull(null);
- sessionFactory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
- return sessionFactory.getObject();
- }
- @Bean
- public SqlSessionTemplate coreSqlSessionTemplate(@Qualifier("coreSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
- return new SqlSessionTemplate(sqlSessionFactory);
- }
- //======schedule========
- @Bean
- @ConfigurationProperties(prefix = "spring.datasource.druid.schedule")
- public DataSource scheduleDataSource(){
- return DruidDataSourceBuilder.create().build();
- }
- @Bean
- public SqlSessionFactory scheduleSqlSessionFactory(@Qualifier("scheduleDataSource") DataSource coreDataSource) throws Exception {
- SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
- sessionFactory.setDataSource(coreDataSource);
- sessionFactory.getObject().getConfiguration().setJdbcTypeForNull(null);
- sessionFactory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
- return sessionFactory.getObject();
- }
- @Bean
- public SqlSessionTemplate scheduleSqlSessionTemplate(@Qualifier("scheduleSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
- return new SqlSessionTemplate(sqlSessionFactory);
- }
- }
第二种是动态数据源模式, 通过 AOP 切入注解引导使用何数据源. 用自定义注解 @interface 来标识方法走对应的数据源.
注意事项: 类中的方法再调用带数据源的方法, 不能被 AOP 切入
- @Target({ElementType.METHOD, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface TargetDataSource {
- String value();
- }
extends spring 的动态 DataSource 路由来匹配
- public class DynamicDataSource extends AbstractRoutingDataSource {
- @Override
- protected Object determineCurrentLookupKey() {
- return DataSourceContextRouting.getDataSourceName();
- }
- }
- @Configuration
- //@EnableConfigurationProperties(MybatisProperties.class)// 不要使用此公共配置, Configuration 会破坏相关 dataSource 的配置
- @MapperScan("com.zss.two.mapper")
- public class MybatisConfig {
- @Bean
- @ConfigurationProperties(prefix = "spring.datasource.druid.core")
- public DataSource coreDataSource() {
- return DruidDataSourceBuilder.create().build();
- }
- @Bean
- @ConfigurationProperties(prefix = "spring.datasource.druid.schedule")
- public DataSource scheduleDataSource() {
- return DruidDataSourceBuilder.create().build();
- }
- @Autowired
- @Qualifier("coreDataSource")
- private DataSource coreDataSource;
- @Autowired
- @Qualifier("scheduleDataSource")
- private DataSource scheduleDataSource;
- @Bean
- public DynamicDataSource dataSource() {
- Map<Object, Object> targetDataSources = new HashMap<>();
- targetDataSources.put(DataSourceConstants.CORE_DATA_SOURCE, coreDataSource);
- targetDataSources.put(DataSourceConstants.SCHEDULE_DATA_SOURCE, scheduleDataSource);
- DynamicDataSource dataSource = new DynamicDataSource();
- // 设置数据源映射
- dataSource.setTargetDataSources(targetDataSources);
- //// 设置默认数据源, 当无法映射到数据源时会使用默认数据源
- dataSource.setDefaultTargetDataSource(coreDataSource);
- dataSource.afterPropertiesSet();
- return dataSource;
- }
- /**
- * 根据数据源创建 SqlSessionFactory
- */
- @Bean
- public SqlSessionFactory sqlSessionFactory(DynamicDataSource dataSource) throws Exception {
- SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
- sessionFactory.setDataSource(dataSource);
- sessionFactory.getObject().getConfiguration().setJdbcTypeForNull(null);
- sessionFactory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
- return sessionFactory.getObject();
- }
- @Bean
- public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {
- return new SqlSessionTemplate(sqlSessionFactory);
- }
第三种, 自定义 Bean 命名策略, 按 beanName 进行自动匹配使用数据源
- @Component
- public class CoreBeanNameGenerator implements BeanNameGenerator {
- @Override
- public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
- return "core"+ ClassUtils.getShortName(definition.getBeanClassName());
- }
- }
- @Component
- public class ScheduleBeanNameGenerator implements BeanNameGenerator {
- @Override
- public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
- return "schedule"+ ClassUtils.getShortName(definition.getBeanClassName());
- }
- }
使用 mybatis MapperScannerConfigurer 自动扫描, 将 Mapper 接口生成注入到 spring
- @Bean
- public MapperScannerConfigurer coreMapperScannerConfig(CoreBeanNameGenerator coreBeanNameGenerator){
- MapperScannerConfigurer configurer = new MapperScannerConfigurer();
- configurer.setNameGenerator(coreBeanNameGenerator);
- configurer.setBasePackage("com.zss.third.mapper.core,com.zss.third.mapper.order");
- configurer.setSqlSessionFactoryBeanName("coreSqlSessionFactory");
- configurer.setSqlSessionTemplateBeanName("coreSqlSessionTemplate");
- return configurer;
- }
- @Bean
- public MapperScannerConfigurer scheduleMapperScannerConfig(ScheduleBeanNameGenerator scheduleBeanNameGenerator){
- MapperScannerConfigurer configurer = new MapperScannerConfigurer();
- configurer.setNameGenerator(scheduleBeanNameGenerator);
- configurer.setBasePackage("com.zss.third.mapper.schedule,com.zss.third.mapper.order");
- configurer.setSqlSessionFactoryBeanName("scheduleSqlSessionFactory");
- configurer.setSqlSessionTemplateBeanName("scheduleSqlSessionTemplate");
- return configurer;
- }
到此, 三种多数据源匹配主要点介绍完, 详细直接下载 GitHub 项目. 在 resources/db 含有相关测试表及数据脚本.
来源: https://www.cnblogs.com/song27/p/12595603.html