springboot2.0实现多数据源,springboot2多数据源
00-1010应用场景集成方案简介
00-1010其实目前市场上有很多成熟的开源中间件,比如:MyCAT、Cobar、sharding-JDBC等。针对子数据库、子表的场景。
主要介绍一种基于springboot的多数据源切换的轻量级集成方案,可用于小型应用。我以前在项目中使用过,因为它简单,易于扩展和优化。
00-1010我们假设目前有以下数据访问场景:1。一个业务逻辑操作来自不同数据库的数据(可能这个场景在你的系统里是不存在的,目前是微服务的架构。每个微服务基本对应一个数据库,其他的需要通过服务提供。), 2.访问子数据库和子表的场景;下面的文章将分别介绍子数据库和子表的集成。
假设在这里,我们用6个图书馆来介绍,每个图书馆1000桌),因为随着业务量的增长,一个图书馆很难抵挡这么大的访问量。比如订单表,我们可以根据userid来划分数据库和表。存储库策略:userId%6[表的数量];库和表的策略:库路由[userId/(6*1000)/1000],表路由[userid/(6 * 1000) 00]。
00-1010方案概述:该方案基于springjdbc中提供的api。看下面两段代码,这是我们的起点。我们都围绕这两个核心方法进行集成。
第一个代码是注册逻辑,它将两个数据源对象defaultTargetDataSource和targetDataSources注册到resolvedDataSources中。
第二段是具体的切换逻辑:如果数据源为空,那么他会寻找我们默认的数据源defaultTargetDataSource如果设置了数据源,那么他会读取值object lookup key=determinecurrentlookup key();我们稍后将重写这个方法。
我们将在配置文件中配置主数据源(默认数据源)和其他数据源,然后在应用启动时在spring容器中注册,分别为defaultTargetDataSource和targetDataSources赋值,然后注册Bean。
在实际使用过程中,我们定义注释,通过节定位当前线程访问的是哪个数据源(维护一个ThreadLocal对象),然后调用determineCurrentLookupKey方法切换数据源。
public void afterPropertiesSet(){ if(this . target data sources==null){ throw new IllegalArgumentException( Property target data sources 是必需的));} this . resolved data sources=new hashmap object,data source(this . targetdata sources . size());对于(图。EntryObject,Object entry : this . targetdata sources . entry set()){ Object lookup key=resolveSpecifiedLookupKey(entry . getkey());data source data source=resolvespecifieddata source(entry . getvalue());this . resolveddatasources . put(lookup key,data source);if(this . defaulttargetdata source!=null){ this . resolveddefaultdata source=resolveSpecifiedDataSource(this . defaulttargetdata source);} @ override public Connection getConnection()抛出SQLException { return determinetargetdata source()。getConnection();} protected DataSource determinetargetdata source(){ assert . not null(this . resolved data sources, data source路由器未初始化);object lookup key=determineCurrentLookupKey();数据源数据
Source = this.resolvedDataSources.get(lookupKey); if (dataSource == null && (this.lenientFallback lookupKey == null)) { dataSource = this.resolvedDefaultDataSource; if (dataSource == null) { throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); return dataSource;1.动态数据源注册注册默认数据源以及其他额外的数据源; 这里只是复制了核心的几个方法,具体的大家下载git看代码。
// 创建DynamicDataSource GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DyncRouteDataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); //默认数据源 mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource); //其他数据源 mpv.addPropertyValue("targetDataSources", targetDataSources); //注册到spring bean容器中 registry.registerBeanDefinition("dataSource", beanDefinition);
2.在Application中加载动态数据源,这样spring容器启动的时候就会将数据源加载到内存中。
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, MybatisAutoConfiguration.class })@Import(DyncDataSourceRegister.class)@ServletComponentScan@ComponentScan(basePackages = { "com.happy.springboot.api","com.happy.springboot.service"})public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}
3.数据源切换核心逻辑:创建一个数据源切换的注解,并且基于该注解实现切面逻辑,这里我们通过threadLocal来实现线程间的数据源切换;
import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target({ ElementType.TYPE, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface TargetDataSource { String value(); // 是否分库 boolean isSharding() default false; // 获取分库策略 String strategy() default "";}// 动态数据源上下文public class DyncDataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static List<String> dataSourceNames = new ArrayList<String>(); public static void setDataSource(String dataSourceName) { contextHolder.set(dataSourceName); } public static String getDataSource() { return contextHolder.get(); public static void clearDataSource() { contextHolder.remove(); public static boolean containsDataSource(String dataSourceName) { return dataSourceNames.contains(dataSourceName);/** * * @author jasoHsu * 动态数据源在切换,将数据源设置到ThreadLocal对象中 */@Component@Order(-10)@Aspectpublic class DyncDataSourceInterceptor { @Before("@annotation(targetDataSource)") public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Exception { String dbIndx = null; String targetDataSourceName = targetDataSource.value() + (dbIndx == null ? "" : dbIndx); if (DyncDataSourceContextHolder.containsDataSource(targetDataSourceName)) { DyncDataSourceContextHolder.setDataSource(targetDataSourceName); } @After("@annotation(targetDataSource)") public void resetDataSource(JoinPoint point, TargetDataSource targetDataSource) { DyncDataSourceContextHolder.clearDataSource();//public class DyncRouteDataSource extends AbstractRoutingDataSource {@Override protected Object determineCurrentLookupKey() { return DyncDataSourceContextHolder.getDataSource(); public DataSource findTargetDataSource() { return this.determineTargetDataSource();
4.与mybatis集成,其实这一步与前面的基本一致。只是将在sessionFactory以及数据管理器中,将动态数据源注册进去【dynamicRouteDataSource】,而非之前的静态数据源【getMasterDataSource()】
@Resource DyncRouteDataSource dynamicRouteDataSource; @Bean(name = "sqlSessionFactory") public SqlSessionFactory getinvestListingSqlSessionFactory() throws Exception { String configLocation = "classpath:/conf/mybatis/configuration.xml"; String mapperLocation = "classpath*:/com/happy/springboot/service/dao/*/*Mapper.xml"; SqlSessionFactory sqlSessionFactory = createDefaultSqlSessionFactory(dynamicRouteDataSource, configLocation, mapperLocation); return sqlSessionFactory; } @Bean(name = "txManager") public PlatformTransactionManager txManager() { return new DataSourceTransactionManager(dynamicRouteDataSource);
5.核心配置参数:
spring: dataSource: happyboot: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/happy_springboot?characterEncoding=utf8&useSSL=true username: root password: admin extdsnames: happyboot01,happyboot02 happyboot01: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/happyboot01?characterEncoding=utf8&useSSL=true username: root password: admin happyboot02: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/happyboot02?characterEncoding=utf8&useSSL=true username: root password: admin
6.应用代码
@Servicepublic class UserExtServiceImpl implements IUserExtService { @Autowired private UserInfoMapper userInfoMapper; @TargetDataSource(value="happyboot01") @Override public UserInfo getUserBy(Long id) { return userInfoMapper.selectByPrimaryKey(id); }}
到此这篇关于关于SpringBoot中的多数据源集成的文章就介绍到这了,更多相关SpringBoot多数据源集成内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。