springboot+mybatis的多数据源配置
1.链接数据库yml配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | spring: datasource: master: jdbc-url: jdbc:mysql: //localhost:3306/dams?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true username: root password: root driver- class -name: com.mysql.jdbc.Driver slave1: jdbc-url: jdbc:mysql: //localhost:3306/root?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true username: root password: root driver- class -name: com.mysql.jdbc.Driver slave2: jdbc-url: jdbc:mysql: //localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true username: root password: root driver- class -name: com.mysql.jdbc.Driver |
2.数据源配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | /** * * 关于数据源配置,参考SpringBoot官方文档第79章《Data Access》 * 79. Data Access * * 79.1 Configure a Custom DataSource * 79.2 Configure Two DataSources */ @Configuration public class DataSourceConfig { //将master数据源注入 @Bean @Primary @ConfigurationProperties ( "spring.datasource.master" ) public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } //将slave1注入 @Bean @ConfigurationProperties ( "spring.datasource.slave1" ) public DataSource slave1DataSource() { return DataSourceBuilder.create().build(); } //将slave2注入 @Bean @ConfigurationProperties ( "spring.datasource.slave2" ) public DataSource slave2DataSource() { return DataSourceBuilder.create().build(); } // 我们配置了4个数据源,1个master,2两个slave,1个路由数据源。前3个数据源都是为了生成第4个数据源,而且后续我们只用这最后一个路由数据源。 @Bean public DataSource myRoutingDataSource( @Qualifier ( "masterDataSource" ) DataSource masterDataSource, @Qualifier ( "slave1DataSource" ) DataSource slave1DataSource, @Qualifier ( "slave2DataSource" ) DataSource slave2DataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DBTypeEnum.MASTER, masterDataSource); targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource); targetDataSources.put(DBTypeEnum.SLAVE2, slave2DataSource); MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource(); myRoutingDataSource.setDefaultTargetDataSource(masterDataSource); // 主库数据源 myRoutingDataSource.setTargetDataSources(targetDataSources); // 路由数据源 return myRoutingDataSource; } } |
3.SqlSessionFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @EnableTransactionManagement @Configuration public class MyBatisConfig { @Resource (name = "myRoutingDataSource" ) private DataSource myRoutingDataSource; //将数据源设置到SqlSessionFactory @Bean public SqlSessionFactory sqlSessionFactory( MybatisProperties mybatisProperties) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(myRoutingDataSource); sqlSessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources( "classpath:mapper/*.xml" )); sqlSessionFactoryBean.setConfiguration(mybatisProperties.getConfiguration()); return sqlSessionFactoryBean.getObject(); } //将数据源设置到事务平台管理器 @Bean public PlatformTransactionManager platformTransactionManager() { return new DataSourceTransactionManager(myRoutingDataSource); } } |
4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | public enum DBTypeEnum { MASTER,SLAVE1,SLAVE2; } //定义注解用来强制访问主库 public @interface Master { } //数据库切换 public class DBContextHolder { //定义变量副本 private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>(); //定义安全自增 private static final AtomicInteger counter = new AtomicInteger(- 1 ); public static void set(DBTypeEnum dbType) { contextHolder.set(dbType); } public static DBTypeEnum get() { return contextHolder.get(); } public static void master() { set(DBTypeEnum.MASTER); System.out.println( "切换到master" ); } public static void slave() { // 轮询切换从数据库 int index = counter.getAndIncrement() % 2 ; if (counter.get() > 9999 ) { counter.set(- 1 ); } if (index == 0 ) { set(DBTypeEnum.SLAVE1); System.out.println( "切换到slave1" ); } else { set(DBTypeEnum.SLAVE2); System.out.println( "切换到slave2" ); } } } |
5.
1 2 3 4 5 6 7 8 9 | public class MyRoutingDataSource extends AbstractRoutingDataSource { //根据路由来确定map中的sqlsessionFactory @Nullable @Override protected Object determineCurrentLookupKey() { return DBContextHolder.get(); } } |
6.注入多数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class DataSourceAop { @Pointcut ( "!@annotation(com.tl.base.datasource.annotation.Master) " + "&& (execution(* com.tl.base.domain..*.query*(..)) " + "|| execution(* com.tl.base.domain..*.get*(..)))" ) public void readPointcut() { } @Pointcut ( "@annotation(com.tl.base.datasource.annotation.Master) " + "|| execution(* com.tl.base.domain..*.insert*(..)) " + "|| execution(* com.tl.base.domain..*.add*(..)) " + "|| execution(* com.tl.base.domain..*.update*(..)) " + "|| execution(* com.tl.base.domain..*.edit*(..)) " + "|| execution(* com.tl.base.domain..*.delete*(..)) " + "|| execution(* com.tl.base.domain..*.remove*(..))" ) public void writePointcut() { } @Before ( "readPointcut()" ) public void read() { DBContextHolder.slave(); } @Before ( "writePointcut()" ) public void write() { DBContextHolder.master(); } |
如果你不知道自己要去哪里,那么去哪里都是一样
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构