springboot-mybatis 多数据源

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

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 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖
  • MyBatis

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

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

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

    692 引用 • 535 回帖

相关帖子

欢迎来到这里!

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

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