当前位置: 首页 > Web前端 > HTML

谈谈Spring Boot 数据源加载及其多数据源简单实现

时间:2023-04-03 01:03:43 HTML

说说SpringBoot数据源加载及其对多数据源的简单实现,实时效果附录效果图实现思路本文提供的方法仅针对类似的简单业务场景。在生产环境和复杂的业务场景,请使用分库分表的中间件(如mycat)或者框架sharding-sphere(一直在用)等。我们先来看看Spring默认的数据源注入策略.以下代码中的默认事务管理器在初始化期间返回加载数据源实现。这里是我们动态数据源的入口//默认事务管理器ppublicclassDataSourceTransactionManagerextendsAbstractPlatformTransactionManagerimplementsResourceTransactionManager,InitializingBean{//在启动时注入一个数据源publicvoidsetDataSource(@NullableDataSourcedataSource){if(dataSourceinstanceofTransactionProxy)DataSource{this.dataSource=((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();}else{this.dataSource=dataSource;}}”是通过注入一个新的DataSourceTransactionManager并为其设置多个DataSource来实现多数据源。Spring默认提供的路由数据源字段publicabstractclassAbstractRoutingDataSourceextendsAbstractDataSourceimplementsInitializingBean{//设置的所有数据源配置user@NullableprivateMaptargetDataSources;//默认数据源配置@NullableprivateObjectdefaultTargetDataSource;//路由键查找实现privateDataSourceLookupdataSourceLookup=newJndiDataSourceLookup();//最后生效的数据源配置(一般清除上面用户对应的设置)@NullableprivateMapresolvedDataSources;}开始实现AbstractRoutingDataSource,设置一个动态数据源实现,只需要实现它的routingkey查找方法可以这里的routingkey对应resolvedDataSourcesMap的key。@Slf4jpublicclassDynamicDataSourceextendsAbstractRoutingDataSource{/***指定路由键。在threadLocal中获取targetkey非常简单。*@return*/@OverrideprotectedObjectdetermineCurrentLookupKey(){returnDynamicDataSourceContextHolder.getDataSourceType();}}}将我们的动态数据源实现注入到Spring的事务管理器中,去数据库查询所有的数据源信息,定义每一个具体的数据源来实现我这里使用的HikariDataSource并赋值给他等@Slf4j@Configuration@AllArgsConstructorpublic类DynamicDataSourceConfig实现TransactionManagementConfigurer{privatefinalMapdataSourceMap=newHashMap<>(8);私有最终数据源属性数据源属性;@Bean("dynamicDataSource")publicDynamicDataSourcedataSource(){JdbcTemplate(dds).queryForList(DataSourceConstant.QUERY_DS_SQL);log.info("开始->初始化动态数据源");Optional.of(dbList).ifPresent(list->list.forEach(db->{log.info("数据源:{}",db.get(DataSourceConstant.DS_NAME));HikariDataSourceds=newHikariDataSource();dataSourceMap.put(db.get(DataSourceConstant.DS_ROUTE_KEY),ds);}));DynamicDataSourceds=newDynamicDataSource();ds.setTargetDataSources(dataSourceMap);returnds;}@BeanpublicPlatformTransactionManagertxManager(){returnnewDataSourceTransactionManager(dataSource());}@OverridepublicPlatformTransactionManagerannotationDrivenTransactionManager(){returntxManager();前面选择的数据源keydesk可以在业务类中保存为TTL,路由数据源会根据DynamicDataSourceContextHolder.setDataSourceType(key)自动选择。当然也可以基于AOP自定义注解等实现。如何动态配置上面的动态数据源其实已经完成了我们想要的功能都有了,但是有什么问题呢,我们在数据源管理平面维护数据源,动态修改ying这个dataSourceMap其实是无效的,不能实时刷新。看一下AbstractRoutingDataSource源码的loadingmap数据源,初始化时只调用afterPropertiesSet到初始数据源map。然后我们只需要获取当前的DynamicDataSourcebean并手动调用afterPropertiesSet即可。整个代码如下publicclassDynamicDataSourceConfigimplementsTransactionManagementConfigurer{privatefinalMapdataSourceMap=newHashMap<>(8);私有最终数据源属性数据源属性;私有最终StringEncryptorstringEncryptor;@Bean("dynamicDataSource")publicDynamicDataSourcedataSource(){DynamicDataSourceds=newDynamicDataSource();HikariDataSourcecads=newHikariDataSource();cads.setJdbcUrl(dataSourceProperties.getUrl());cads.setDriverClassName(dataSourceProperties.getDriverClassName());cads.setUsername(dataSourceProperties.getUsername()).setPassword(dataSourceProperties.getPassword());ds.setDefaultTargetDataSource(cads);dataSourceMap.put(0,cads);ds.setTargetDataSources(dataSourceMap);返回ds;}/***组装默认配置的数据源和查询数据库配置*/@PostConstructpublicvoidinit(){DriverManagerDataSourcedds=newDriverManagerDataSource();dds.setUrl(dataSourceProperties.getUrl());dds.setDriverClassName(dataSourceProperties.getDriverClassName());dds.setUsername(dataSourceProperties.getUsername());dds.setPassword(dataSourceProperties.getPassword());List>dbList=newJdbcTemplate(dds).queryForList(DataSourceConstant.QUERY_DS_SQL);log.info("开始->初始化动态数据源");Optional.of(dbList).ifPresent(list->list.forEach(db->{log.info("数据源:{}",db.get(DataSourceConstant.DS_NAME));HikariDataSourceds=newHikariDataSource();ds.setJdbcUrl(String.valueOf(db.get(DataSourceConstant.DS_JDBC_URL)));ds.setDriverClassName(Driver.class.getName());ds.setUsername((String)db.get(DataSourceConstant.DS_USER_NAME));字符串decPwd=stringEncryptor.decrypt((字符串)db.get(DataSourceConstant.DS_USER_PWD));ds.setPassword(decPwd);dataSourceMap.put(db.get(DataSourceConstant.DS_ROUTE_KEY),ds);}));log.info("完成->初始化动态数据源,共{}项",dataSourceMap.size());}/***重新加载数据源配置*/publicBooleanreload(){init();DynamicDataSource数据源=数据源();dataSource.setTargetDataSources(dataSourceMap);数据源.afterPropertiesSet();返回Boolean.FALSE;}@BeanpublicPlatformTransactionManagertxManager(){返回新的DataSourceTransactionManager(dataSource());}@OverridepublicPlatformTransactionManagerannotationDrivenTransactionManager(){returntxManager();开发一个基于vue前后端分离的开发平台QQ:2270033969说说你是如何使用springcloud的。欢迎关注我们,获取更多有趣的JavaEE实践