springboot-mybatis 多数据源

本贴最后更新于 1655 天前,其中的信息可能已经时移世改

springboot-mybatis 多数据源

多数据源的目的

  • 随着业务的发展,单库的的形式已经无法承受高并发所带来的压力,一个项目使用多个数据库显得额外重要,比如读写分离、主从复制

mybatis 配置多数据源的方式

分包的方式

  • 将不同数据源的 mapper 文件分开
  • 读数据源
  @Configuration
  @MapperScan(basePackages="com.yechuan.dao.read",sqlSessionFactoryRef="readSqlSessionFactory")
  public class ReadDataSourceConfig {
	@Primary
	@Bean(name = "readDataSource")
	@ConfigurationProperties("datasources.read")
		public DataSource readDataSource() {
		return new DruidDataSource();
	}

	@Primary
	@Bean(name = "readDataSourceTransactionManager")
	public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
	    return new DataSourceTransactionManager(dataSource);
	}

	@Primary
	@Bean(name = "readSqlSessionFactory")
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
	    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
	    sqlSessionFactoryBean.setDataSource(dataSource);
	    return sqlSessionFactoryBean.getObject();
	}

	@Primary
	@Bean(name = "readSqlSessionTemplate")
	public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
	    return new SqlSessionTemplate(sqlSessionFactory);
	}

   }

  • 写数据源
@Configuration
@MapperScan(basePackages = "com.yechuan.dao.write", sqlSessionFactoryRef = "writeSqlSessionFactory")
public class WriteDataSourceConfig {

    @Bean(name = "writeDataSource")
    @ConfigurationProperties("datasources.write")
    public DataSource writeDataSource() {
        return new DruidDataSource();

    }

    @Bean(name = "writeDataSourceTransactionManager")
    public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("writeDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "writeSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("writeDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean.getObject();
    }

    @Bean(name = "writeSqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("writeSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

AOP

  • 利用 aop 原理,在访问数据库之前,替换数据库实例
  • 在访问数据库的时候,会访问 AbstractRoutingDataSourcedetermineCurrentLookupKey 方法来获取数据库的实例 key
  1. 定义一个枚举类来说明一下当前数据源实例 key 有哪些

    public enum DataSourceKey {
    	READ("read"),
    	WRITE("write");
    
    	private String name;
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	private DataSourceKey(String name) {
    		this.name = name;
    	}
    }
    
  2. 创建一个工具类来实现,存储与获取数据源 key

    public class DynamicDataSourceContextHolder {
    	/**
    	*
    	* 保证其线程安全
    	*/
    	private static ThreadLocal<Object> CONTEXT_HOLDER = ThreadLocal.withInitial(() -> DataSourceKey.READ.getName());
    	/**
    	*
    	* 存储
    	*/
    	public static void setDataSourceKey(String key){
    		CONTEXT_HOLDER.set(key);
    	}
    	/**
    	*
    	* 获取
    	*/
    	public static Object getDataSourceKey(){
    		return CONTEXT_HOLDER.get();
    	}
    	/**
    	*
    	* 删除
    	*/
    	public static void clearDataSourceKey(){
    		CONTEXT_HOLDER.remove();
    	}
    
    }
    
  3. 重写 AbstractRoutingDataSource

    @Slf4j
    public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    
    	@Override
    	protected Object determineCurrentLookupKey() {
    		log.info("当前使用的数据源是 : {}",DynamicDataSourceContextHolder.getDataSourceKey());
    		return DynamicDataSourceContextHolder.getDataSourceKey();
    	}
    
    }
    
  4. 配置数据源

    @Configuration
    public class DataSourceConfig {
      @Primary
      @Bean(name = "readDataSource")
      @ConfigurationProperties("datasources.read")
      public DataSource readDataSource() {
          return new DruidDataSource();
      }
    
      @Bean(name = "writeDataSource")
      @ConfigurationProperties("datasources.write")
      public DataSource writeDataSource() {
            return new DruidDataSource();
    
      }
      @Bean(name = "dynamicDataSource")
      public DataSource dynamicDataSource(@Qualifier(value = "readDataSource")DataSource readDataSource,@Qualifier(value = "writeDataSource")DataSource writeDataSource){
        	DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
    
        	Map<Object, Object> dataSourceMap = new HashMap<Object, Object>(2);
        	dataSourceMap.put(DataSourceKey.READ.getName(),readDataSource);
        	dataSourceMap.put(DataSourceKey.WRITE.getName(),writeDataSource);
    
        	dynamicRoutingDataSource.setDefaultTargetDataSource(readDataSource);
        	dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
    
        	DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
    
        	return dynamicRoutingDataSource;
      }
    
      @Bean
      public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier(value = "dynamicDataSource") DataSource dataSource) {
          return new DataSourceTransactionManager(dataSource);
      }
    
      @Bean
      public SqlSessionFactory sqlSessionFactory(@Qualifier(value = "dynamicDataSource") DataSource dataSource) throws Exception {
          SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
          sqlSessionFactoryBean.setDataSource(dataSource);
          return sqlSessionFactoryBean.getObject();
      }
    
      @Bean
      public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
          return new SqlSessionTemplate(sqlSessionFactory);
      }
    }
    
  5. 自定义注解

    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface UserDataSource {
    	DataSourceKey value() default DataSourceKey.READ;
    }
    
  6. AOP 动态切换数据源

    @Slf4j
    @Aspect
    @Component
    public class DynamicDataSourceAspect {
    
    
    	@Before("@annotation(userDataSource))")
    	public void switchDataSource(JoinPoint joinPoint,UserDataSource userDataSource){
    	DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value().getName());
            log.info("在方法 :[{}] 内切换数据源为: [{}]",
            joinPoint.getSignature(),DynamicDataSourceContextHolder.getDataSourceKey());
    
    	}
    
    	@After("@annotation(userDataSource))")
    	public void restoreDataSource(JoinPoint joinPoint,UserDataSource userDataSource){
            DynamicDataSourceContextHolder.clearDataSourceKey();
            log.info("在方法 [{}] 执行后,恢复数据源 [{}]",
               joinPoint.getSignature(),DynamicDataSourceContextHolder.getDataSourceKey());
    	}
    
    }
    
  7. 在调用的时候使用注解即可

使用 mybatis-puls 与 dynamic

  • 鲁迅先生曾经说过我们不要自己造轮子,要使用别人的轮子
  1. 导入相关依赖

    <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
         <version>2.5.4</version>
    </dependency>
    <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>mybatis-plus-boot-starter</artifactId>
         <version>3.3.1.tmp</version>
    </dependency>
    
  2. yml 配置

    spring:
      datasource:
        dynamic:
          datasource:
            read:
              password: root
              username: root
              url: jdbc:mysql://localhost:3306/sharding_sphere_read
              driverClassName: com.mysql.jdbc.Driver
            write:
              password: root
              username: root
              url: jdbc:mysql://localhost:3306/sharding_sphere_write
              driverClassName: com.mysql.jdbc.Driver
          primary: read
          strict: true
    
  3. 使用

        @DS("write")
        @Transactional(rollbackFor = Exception.class)
        public void addUser( UserDto userDto) {
            userDao.insert(userDto);
        }
    
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    170 引用 • 414 回帖 • 387 关注
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    690 引用 • 535 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...