当前位置: 首页 > news >正文

域名注册网站查询工具杭州企业建站程序

域名注册网站查询工具,杭州企业建站程序,邗江区做网站,php做网站特效最近在做业务需求时#xff0c;需要从不同的数据库中获取数据然后写入到当前数据库中#xff0c;因此涉及到切换数据源问题。本来想着使用Mybatis-plus中提供的动态数据源SpringBoot的starter#xff1a;dynamic-datasource-spring-boot-starter来实现。 结果引入后发现由于…最近在做业务需求时需要从不同的数据库中获取数据然后写入到当前数据库中因此涉及到切换数据源问题。本来想着使用Mybatis-plus中提供的动态数据源SpringBoot的starterdynamic-datasource-spring-boot-starter来实现。 结果引入后发现由于之前项目环境问题导致无法使用。然后研究了下数据源切换代码决定自己采用ThreadLocalAbstractRoutingDataSource来模拟实现dynamic-datasource-spring-boot-starter中线程数据源切换。 1 简介 上述提到了ThreadLocal和AbstractRoutingDataSource我们来对其进行简单介绍下。 ThreadLocal想必大家必不会陌生全称thread local variable。主要是为解决多线程时由于并发而产生数据不一致问题。ThreadLocal为每个线程提供变量副本确保每个线程在某一时间访问到的不是同一个对象这样做到了隔离性增加了内存但大大减少了线程同步时的性能消耗减少了线程并发控制的复杂程度。 ThreadLocal作用在一个线程中共享不同线程间隔离 ThreadLocal原理ThreadLocal存入值时会获取当前线程实例作为key存入当前线程对象中的Map中。 AbstractRoutingDataSource根据用户定义的规则选择当前的数据源 作用在执行查询之前设置使用的数据源实现动态路由的数据源在每次数据库查询操作前执行它的抽象方法determineCurrentLookupKey()决定使用哪个数据源。 2 代码实现 程序环境 SpringBoot2.4.8Mybatis-plus3.2.0Druid1.2.6lombok1.18.20commons-lang3 3.102.1 实现ThreadLocal 创建一个类用于实现ThreadLocal主要是通过getsetremove方法来获取、设置、删除当前线程对应的数据源。 public class DataSourceContextHolder {//此类提供线程局部变量。这些变量不同于它们的正常对应关系是每个线程访问一个线程(通过get、set方法),有自己的独立初始化变量的副本。private static final ThreadLocalString DATASOURCE_HOLDER new ThreadLocal();/*** 设置数据源* param dataSourceName 数据源名称*/public static void setDataSource(String dataSourceName){DATASOURCE_HOLDER.set(dataSourceName);}/*** 获取当前线程的数据源* return 数据源名称*/public static String getDataSource(){return DATASOURCE_HOLDER.get();}/*** 删除当前数据源*/public static void removeDataSource(){DATASOURCE_HOLDER.remove();}}2.2 实现AbstractRoutingDataSource 定义一个动态数据源类实现AbstractRoutingDataSource通过determineCurrentLookupKey方法与上述实现的ThreadLocal类中的get方法进行关联实现动态切换数据源。 public class DynamicDataSource extends AbstractRoutingDataSource {public DynamicDataSource(DataSource defaultDataSource,MapObject, Object targetDataSources){super.setDefaultTargetDataSource(defaultDataSource);super.setTargetDataSources(targetDataSources);}Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSource();} }上述代码中还实现了一个动态数据源类的构造方法主要是为了设置默认数据源以及以Map保存的各种目标数据源。其中Map的key是设置的数据源名称value则是对应的数据源DataSource。 2.3 配置数据库 application.yml中配置数据库信息 #设置数据源 spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:master:url: jdbc:mysql://xxxxxx:3306/test1?characterEncodingutf-8allowMultiQueriestruezeroDateTimeBehaviorconvertToNulluseSSLfalseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave:url: jdbc:mysql://xxxxx:3306/test2?characterEncodingutf-8allowMultiQueriestruezeroDateTimeBehaviorconvertToNulluseSSLfalseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverinitial-size: 15min-idle: 15max-active: 200max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: test-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: falseconnection-properties: false//设置数据源 public class DateSourceConfig {BeanConfigurationProperties(spring.datasource.druid.master)public DataSource masterDataSource(){return DruidDataSourceBuilder.create().build();}BeanConfigurationProperties(spring.datasource.druid.slave)public DataSource slaveDataSource(){return DruidDataSourceBuilder.create().build();}Bean(name dynamicDataSource)Primarypublic DynamicDataSource createDynamicDataSource(){MapObject,Object dataSourceMap new HashMap();DataSource defaultDataSource masterDataSource();dataSourceMap.put(master,defaultDataSource);dataSourceMap.put(slave,slaveDataSource());return new DynamicDataSource(defaultDataSource,dataSourceMap);}}通过配置类将配置文件中的配置的数据库信息转换成datasource并添加到DynamicDataSource中同时通过Bean将DynamicDataSource注入Spring中进行管理后期在进行动态数据源添加时会用到。 2.4 测试 在主从两个测试库中分别添加一张表test_user里面只有一个字段user_name。 create table test_user(user_name varchar(255) not null comment 用户名 ) 在主库添加信息insert into test_user (user_name) value (master); 从库中添加信息insert into test_user (user_name) value (slave);我们创建一个getData的方法参数就是需要查询数据的数据源名称。 GetMapping(/getData.do/{datasourceName}) public String getMasterData(PathVariable(datasourceName) String datasourceName){DataSourceContextHolder.setDataSource(datasourceName);TestUser testUser testUserMapper.selectOne(null);DataSourceContextHolder.removeDataSource();return testUser.getUserName(); }其他的Mapper和实体类大家自行实现。 执行结果 1、传递master时 2、传递slave时 通过执行结果我们看到传递不同的数据源名称查询对应的数据库是不一样的返回结果也不一样。 在上述代码中我们看到DataSourceContextHolder.setDataSource(datasourceName); 来设置了当前线程需要查询的数据库通过DataSourceContextHolder.removeDataSource(); 来移除当前线程已设置的数据源。使用过Mybatis-plus动态数据源的小伙伴应该还记得我们在使用切换数据源时会使用到DynamicDataSourceContextHolder.push(String ds); 和DynamicDataSourceContextHolder.poll(); 这两个方法翻看源码我们会发现其实就是在使用ThreadLocal时使用了栈这样的好处就是能使用多数据源嵌套这里就不带大家实现了有兴趣的小伙伴可以看看Mybatis-plus中动态数据源的源码。 注启动程序时小伙伴不要忘记将SpringBoot自动添加数据源进行排除哦否则会报循环依赖问题。 SpringBootApplication(exclude DataSourceAutoConfiguration.class) 2.5 优化调整 2.5.1 注解切换数据源 在上述中虽然已经实现了动态切换数据源但是我们会发现如果涉及到多个业务进行切换数据源的话我们就需要在每一个实现类中添加这一段代码。 说到这有小伙伴应该就会想到使用注解来进行优化接下来我们来实现一下。 2.5.1.1 定义注解 我们就用mybatis动态数据源切换的注解DS代码如下 Target({ElementType.METHOD,ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented Inherited public interface DS {String value() default master; }2.5.1.2 实现aop Aspect Component Slf4j public class DSAspect {Pointcut(annotation(com.jiashn.dynamic_datasource.dynamic.aop.DS))public void dynamicDataSource(){}Around(dynamicDataSource())public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {MethodSignature signature (MethodSignature)point.getSignature();Method method signature.getMethod();DS ds method.getAnnotation(DS.class);if (Objects.nonNull(ds)){DataSourceContextHolder.setDataSource(ds.value());}try {return point.proceed();} finally {DataSourceContextHolder.removeDataSource();}} }代码使用了Around通过ProceedingJoinPoint获取注解信息拿到注解传递值然后设置当前线程的数据源。对aop不了解的小伙伴可以自行google或百度。 2.5.1.3 测试 添加两个测试方法 GetMapping(/getMasterData.do) public String getMasterData(){TestUser testUser testUserMapper.selectOne(null);return testUser.getUserName(); }GetMapping(/getSlaveData.do) DS(slave) public String getSlaveData(){TestUser testUser testUserMapper.selectOne(null);return testUser.getUserName(); }由于DS中设置的默认值是master因此在调用主数据源时可以不用进行添加。 执行结果 1、调用getMasterData.do方法 2、调用getSlaveData.do方法 通过执行结果我们通过DS也进行了数据源的切换实现了Mybatis-plus动态切换数据源中的通过注解切换数据源的方式。 2.5.2 动态添加数据源 业务场景 有时候我们的业务会要求我们从保存有其他数据源的数据库表中添加这些数据源然后再根据不同的情况切换这些数据源。 因此我们需要改造下DynamicDataSource来实现动态加载数据源。 2.5.2.1 数据源实体 Data Accessors(chain true) public class DataSourceEntity {/*** 数据库地址*/private String url;/*** 数据库用户名*/private String userName;/*** 密码*/private String passWord;/*** 数据库驱动*/private String driverClassName;/*** 数据库key即保存Map中的key*/private String key; }实体中定义数据源的一般信息同时定义一个key用于作为DynamicDataSource中Map中的key。 2.5.2.2 修改DynamicDataSource 实现动态数据源根据AbstractRoutingDataSource路由到不同数据源中 Slf4j public class DynamicDataSource extends AbstractRoutingDataSource {private final MapObject,Object targetDataSourceMap;public DynamicDataSource(DataSource defaultDataSource,MapObject, Object targetDataSources){super.setDefaultTargetDataSource(defaultDataSource);super.setTargetDataSources(targetDataSources);this.targetDataSourceMap targetDataSources;}Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSource();}/*** 添加数据源信息* param dataSources 数据源实体集合* return 返回添加结果*/public void createDataSource(ListDataSourceEntity dataSources){try {if (CollectionUtils.isNotEmpty(dataSources)){for (DataSourceEntity ds : dataSources) {//校验数据库是否可以连接Class.forName(ds.getDriverClassName());DriverManager.getConnection(ds.getUrl(),ds.getUserName(),ds.getPassWord());//定义数据源DruidDataSource dataSource new DruidDataSource();BeanUtils.copyProperties(ds,dataSource);//申请连接时执行validationQuery检测连接是否有效这里建议配置为TRUE防止取到的连接不可用dataSource.setTestOnBorrow(true);//建议配置为true不影响性能并且保证安全性。//申请连接的时候检测如果空闲时间大于timeBetweenEvictionRunsMillis执行validationQuery检测连接是否有效。dataSource.setTestWhileIdle(true);//用来检测连接是否有效的sql要求是一个查询语句。dataSource.setValidationQuery(select 1 );dataSource.init();this.targetDataSourceMap.put(ds.getKey(),dataSource);}super.setTargetDataSources(this.targetDataSourceMap);// 将TargetDataSources中的连接信息放入resolvedDataSources管理super.afterPropertiesSet();return Boolean.TRUE;}}catch (ClassNotFoundException | SQLException e) {log.error(---程序报错---:{}, e.getMessage());}return Boolean.FALSE;}/*** 校验数据源是否存在* param key 数据源保存的key* return 返回结果true存在false不存在*/public boolean existsDataSource(String key){return Objects.nonNull(this.targetDataSourceMap.get(key));} }在改造后的DynamicDataSource中我们添加可以一个 private final MapObject,Object targetDataSourceMap这个map会在添加数据源的配置文件时将创建的Map数据源信息通过DynamicDataSource构造方法进行初始赋值即DateSourceConfig类中的createDynamicDataSource()方法中。 同时我们在该类中添加了一个createDataSource方法进行数据源的创建并添加到map中再通过super.setTargetDataSources(this.targetDataSourceMap) ;进行目标数据源的重新赋值。 2.5.2.3 动态添加数据源 上述代码已经实现了添加数据源的方法那么我们来模拟通过从数据库表中添加数据源然后我们通过调用加载数据源的方法将数据源添加进数据源Map中。 在主数据库中定义一个数据库表用于保存数据库信息。 create table test_db_info(id int auto_increment primary key not null comment 主键Id,url varchar(255) not null comment 数据库URL,username varchar(255) not null comment 用户名,password varchar(255) not null comment 密码,driver_class_name varchar(255) not null comment 数据库驱动name varchar(255) not null comment 数据库名称 )为了方便我们将之前的从库录入到数据库中修改数据库名称。 insert into test_db_info(url, username, password,driver_class_name, name) value (jdbc:mysql://xxxxx:3306/test2?characterEncodingutf-8allowMultiQueriestruezeroDateTimeBehaviorconvertToNulluseSSLfalse,root,123456,com.mysql.cj.jdbc.Driver,add_slave)数据库表对应的实体、mapper小伙伴们自行添加。 启动SpringBoot时添加数据源 Component public class LoadDataSourceRunner implements CommandLineRunner {Resourceprivate DynamicDataSource dynamicDataSource;Resourceprivate TestDbInfoMapper testDbInfoMapper;Overridepublic void run(String... args) throws Exception {ListTestDbInfo testDbInfos testDbInfoMapper.selectList(null);if (CollectionUtils.isNotEmpty(testDbInfos)) {ListDataSourceEntity ds new ArrayList();for (TestDbInfo testDbInfo : testDbInfos) {DataSourceEntity sourceEntity new DataSourceEntity();BeanUtils.copyProperties(testDbInfo,sourceEntity);sourceEntity.setKey(testDbInfo.getName());ds.add(sourceEntity);}dynamicDataSource.createDataSource(ds);}} }经过上述SpringBoot启动后已经将数据库表中的数据添加到动态数据源中我们调用之前的测试方法将数据源名称作为参数传入看看执行结果。
http://www.pierceye.com/news/7337/

相关文章:

  • 网站毕业设计代做工业园网站建设
  • 郑州网站优化的微博_腾讯微博怎么用dw网站怎么建设
  • 孵化器网站建设方案平台经济是什么意思
  • 如何创建一个属于自己的网站推广软文营销案例
  • 做外贸怎么网站找客户通化北京网站建设
  • 做系统和做网站哪个简单一些免费站推广网站2022
  • 私募基金公司网站建设大发 wordpress ifanr
  • 高唐建筑公司网站苏州软件开发
  • 网站建设项目简介培训网站完整页面
  • 万博法务网站怎么优化整站
  • 网站建设是不是要有营业执照宿迁做网站需要多少钱
  • 2018建设网站成立中英文网站建设工作领导小组
  • 外贸网站建设公司价格江苏镇江论坛
  • 做网站买虚拟服务器南京网站如何制作
  • 如何做直接打开网站的二维码郑州专业做网站
  • 中国能源建设集团网站群做腰椎核磁证网站是 收 七
  • 建个门户网站百度提交wordpress
  • 怎么用VS2012建设网站中企动力企业邮箱下载
  • 怎么做搜索功能网站工作总结范文模板大全
  • 私人pk赛车网站怎么做网页制作工具分哪两类
  • 嘉兴 网站 建设东莞网站优化一般多少钱
  • 网站开发竞争对手分析安康市建设局网站
  • 高端网站定制的方法如何做公司的网站建设
  • 合肥网站优化软件金山西安网站建设
  • 学做网站应该看那些书给网站建设提意见
  • 用什么系统程序做评测网站互联网软件开发是什么工作
  • 留电话的广告网站新网站如何做排名
  • 临海网站开发公司电话海尔网站建设目的
  • 中国建设领域专业人员网站网站的推广方式
  • 仿网站视频教程创建一个网站的最常用的方法是先建立一个文件夹