springBoot多数据源使用tdengine(3.0.7.1)+MySQL+mybatisPlus+druid连接池

一、安装部署

1、我这里使用的 3.0.7.1版本,因为我看3.x版本已经发布了一年了,增加了很多新的功能,而且3.x官方推荐,对于2.x的版本,官网都已经推荐进行升级到3.x,所以考虑到项目以后的发展,决定使用3.x版本

2、下载安装包,我这里是linux安装,虽然在3.x版本已经可以支持window安装,但是为了考虑后面的性能测试,后期的部署,所以这里还是使用linux,这里安装步骤,官方说的已经很清楚了,就不再啰嗦了

在这里插入图片描述
3、安装完成后,会将对应的服务安装到系统服务中。我们直接通过 systemctl start xxx来处理即可,官网文档

在这里插入图片描述

4、启动成功后,我们查看服务状态,确实成功的之后

在这里插入图片描述

5、在我们的当前用户的bin目录下,有很多tdengine的可执行文件

在这里插入图片描述

二、远程链接

1、链接之后,链接的方式很多

2、连接器建立连接的方式,TDengine 提供两种:

  • 通过 taosAdapter 组件提供的 REST API 建立与 taosd 的连接,这种连接方式下文中简称“REST 连接”
  • 通过客户端驱动程序 taosc 直接与服务端程序 taosd 建立连接,这种连接方式下文中简称“原生连接”。

3、这里需要说明的一点就是,我们使用 REST 连接,那么在服务端 taosAdapter 的必须得启动,这个如果你不是docker启动,你得手动启动这个服务,不然你就无法使用 REST 连接

就我目前测试来看,只有docker的是创建完成后,会帮你启动 taosAdapter,其它window和linux是没有的,我们需要手动去启动

4、启动/停止 taosAdapter

在 Linux 系统上 taosAdapter 服务默认由 systemd 管理。使用命令 systemctl start taosadapter 可以启动 taosAdapter 服务。使用命令 systemctl stop taosadapter 可以停止 taosAdapter 服务。

在这里插入图片描述

2.1、使用DBeaver 链接

1、下载安装最新的DBeaver

2、点击左上角建立链接,然后选择 TDengine 即可

在这里插入图片描述

3、填写用户名和密码

注意这里可以看到使用工具链接是时,的URL是 TAOS-RS,说明我们的链接方式是 “REST 连接”,所以服务端 taosAdapter 的必须得启动.

在这里插入图片描述
4、这边的驱动链接版本是 3.2.1

在这里插入图片描述
5、链接成功

mp_test 和 bfh 数据库是我自己创建的

在这里插入图片描述

2.2、使用官网客户端 TDengineGUI 链接

1、tdengine 也有做客户端,用于展示,但是体验不太好

在这里插入图片描述
2、点击下载链接 https://github.com/arielyang/TDengineGUI/tags 下载zip ,解压安装即可

在这里插入图片描述
3、链接,方式参考dbeaver 即可

注意这里可以的URL是 TAOS-RS,说明我们的链接方式是 “REST 连接”,所以服务端 taosAdapter 的必须得启动.

在这里插入图片描述

三、springBoot 使用

1、以下的项目中的链接这里使用的都是 原生连接

通过客户端驱动程序 taosc 直接与服务端程序 taosd 建立连接,这种连接方式下文中简称“原生连接”。

2、你也可以使用 RS方式,但是那个方式效率要比原生的低30%

3、只需要把参数 url: jdbc:TAOS://192.168.172.129:6030/mp_test?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8 换成 "jdbc:TAOS-RS://192.168.172.129:6030/mp_test?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8";即可

3.1、使用JDBC的方式(prepareStatement)

1、不通过 dynamic-datasource-spring-boot-starter这个来完成多数据源的切换,就只是简单的配置数据源以及数据库连接池,这里不直接集成tdengine 到 mybatisPlus 的原因有两个

  • 集成到 mybatisPlus 时需要多数据源,无论是使用dynamic-datasource的方案,还是自己去写,都需要去处理做逻辑代码,可能后期需要版本的维护就又要考虑这个维护
  • 第二就是无论这个tdengine 如何发展,它一定会支持 jdbc 的方案并给出最新的demo,但是可能其它示例更具会很慢,甚至不会支持 mybatis 和 springBoot的其它版本
  • 效率问题,官网的 JDBC 对于提升效率一节中说到,我们要批量插入,并给出了相关JDBC的示例,但是如果我们使用mybatisPlus ,可以就要取看下源码,mybatisPlus是否有做多余的操作,增加我们的业务工作量.

当然这只是我个人看法,所以,不愿意使用这个方案的,可以直接跳到 3.2节,使用 dynamic-datasource-spring-boot-starter 来通过mybatiPlus 来管理多个数据源

3.1.1、项目配置及其代码

项目很简单,大家看下就明白了

1、整个项目结构

在这里插入图片描述

2、pom依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.14</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><!-- mybatis plus 依赖 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><!-- lombok插件 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- alibaba的fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><!-- hutool工具 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.5</version></dependency><!-- TDengine 依赖 --><dependency><groupId>com.taosdata.jdbc</groupId><artifactId>taos-jdbcdriver</artifactId><version>3.2.4</version></dependency><!--Druid  方便控制台查看--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency></dependencies>

3、配置文件

# 项目端口
server:port: 7280servlet:# 项目路径context-path: /tadtshutdown: graceful  #开启优雅停机spring:application:name: thermal-api-demonstration-tdenginelifecycle:timeout-per-shutdown-phase: 30s #设置缓冲时间 默认也是30s# Mysql配置datasource:druid:# 接下来(one,two)其实就都是自定义配置了,springBoot是识别不了的(当然你也可以另起其它行,到其它位置),我们需要将这些配置映射到对应的类上,springBoot不会帮我们做one:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/test01?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8username: rootpassword: 123456type: com.alibaba.druid.pool.DruidDataSourcename: mysqlDataSource  # 在druid 内数据源的名称# springboot2.0整合了hikari ,据说这是目前性能最好的java数据库连接池,但是 druid 有控制面板方便查看# 手动配置数据源validation-query: SELECT 1 FROM DUAL  # 连接是否有效的查询语句validation-query-timeout: 60000 # 连接是否有效的查询超时时间# 建议 连接数 = ((核心数 * 2) + 有效磁盘数)initial-size: 40  #初始化时建立物理连接的个数,初始化发生在显示调用 init 方法,或者第一次 getConnection 时min-idle: 40  # 最小连接池数量max-active: 100  #最大连接池数量test-on-borrow: false  #申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetest-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetime-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒min-evictable-idle-time-millis: 300000  # 配置一个连接在池中最小生存的时间,单位是毫秒two:driver-class-name: com.taosdata.jdbc.TSDBDriver# 这里指定了具体的数据库 需要注意,# 如果换成不指定具体数据库名称 jdbc:TAOS://192.168.172.129:6030?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8 则在sql中使用必须要指定数据库的名称 dba.table_burl: jdbc:TAOS://192.168.172.129:6030/mp_test?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8username: rootpassword: taosdatatype: com.alibaba.druid.pool.DruidDataSourcename: tdengineDataSource # 在druid 内数据源的名称# springboot2.0整合了hikari ,据说这是目前性能最好的java数据库连接池,但是 druid 有控制面板方便查看# 手动配置数据源validation-query: select server_status()  # 连接是否有效的查询语句validation-query-timeout: 60000 # 连接是否有效的查询超时时间# 建议 连接数 = ((核心数 * 2) + 有效磁盘数)initial-size: 10  #初始化时建立物理连接的个数,初始化发生在显示调用 init 方法,或者第一次 getConnection 时min-idle: 10  # 最小连接池数量max-active: 20  #最大连接池数量test-on-borrow: false  #申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetest-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetime-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒min-evictable-idle-time-millis: 300000  # 配置一个连接在池中最小生存的时间,单位是毫秒# 热部署devtools:restart:enabled: true# jackson 配置jackson:time-zone: GMT+8date-format: yyyy-MM-dd HH:mm:ss# 上传文件servlet:multipart:max-file-size: 50MBmax-request-size: 50MBlogstash:host: 192.168.172.228  # logstash部署的服务器IPenv: dev  # 将日志加载到elk中项目的环境后缀名称-时间# mybatis配置
mybatis-plus:# xml文件路径mapper-locations: classpath:mapper/*/*.xml# 实体类路径type-aliases-package: com.asurplus.*.entityconfiguration:# 驼峰转换map-underscore-to-camel-case: true# 是否开启缓存cache-enabled: false# 打印sqllog-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 全局配置global-config:# 数据库字段驼峰下划线转换db-column-underline: true# id自增类型(数据库id自增)id-type: 0

4、DataSourceConfig

package cn.jt.thermalapidemonstrationtdengine.config;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;import javax.servlet.Filter;
import javax.sql.DataSource;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年08月01日*/
@Component
@Slf4j
public class DataSourceConfig {public static final String MYSQL_DATA_SOURCE = "mysqlDataSource";public static final String TDENGINE_DATA_SOURCE = "tdengineDataSource";/*** http://127.0.0.1:7280/tadt/druid* 配置Druid的监控视图** @return*/@Beanpublic ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {ServletRegistrationBean<StatViewServlet> registrationBean =new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");// 配置Druid监控页面的登录用户名和密码registrationBean.addInitParameter("loginUsername", "admin");registrationBean.addInitParameter("loginPassword", "123456");// 设置 IP 白名单,允许访问的 IP,多个 IP 用逗号分隔registrationBean.addInitParameter("allow", "127.0.0.1");// 设置 IP 黑名单,拒绝访问的 IP,多个 IP 用逗号分隔(当 IP 在黑名单中同时又在白名单中时,优先于白名单)// servletRegistrationBean.addInitParameter("deny", "192.168.1.100");// 是否能够重置数据registrationBean.addInitParameter("resetEnable", "false");return registrationBean;}/*** 配置Druid的WebStatFilter** @return*/@Beanpublic FilterRegistrationBean<Filter> druidWebStatFilter() {FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new WebStatFilter());// 添加过滤规则registrationBean.addUrlPatterns("/*");// 配置不拦截的路径registrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");return registrationBean;}/*** 自动配置的数据源,使用Druid连接池* druid 会管理这数据源** @return*/@Bean(name = MYSQL_DATA_SOURCE)@Primary@ConfigurationProperties(prefix = "spring.datasource.druid.one")public DataSource dataSource() {return new DruidDataSource();}/*** 手动配置的数据源,也使用Druid连接池* druid 会管理这数据源* <p>* 这里不直接集成tdengine 到 mybatisPlus 的原因有两个* 1、集成到 mybatisPlus 时需要多数据源,无论是使用dynamic-datasource的方案,还是自己去写,都需要去处理做逻辑代码,可能后期需要版本的维护就又要考虑这个维护* 2、就是无论这个tdengine 如何发展,它一定会支持 jdbc 的方案,但是可能不会支持 mybatis和 springBoot,* 3、效率问题** @return*/@Bean(name = TDENGINE_DATA_SOURCE)@ConfigurationProperties(prefix = "spring.datasource.druid.two")public DataSource customDataSource() {return new DruidDataSource();}
}

5、TdEngineController

package cn.jt.thermalapidemonstrationtdengine.controller;import cn.jt.thermalapidemonstrationtdengine.service.TdEngineService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.sql.Timestamp;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月31日*/
@Slf4j
@RestController
@RequestMapping("/td")
public class TdEngineController {@Autowiredprivate TdEngineService tdEngineService;@GetMapping("/mockOne")public String mockOne() {tdEngineService.insertOneByTime(new Timestamp(System.currentTimeMillis()));return "ok";}@GetMapping("/mockMany")public String mockMany() {tdEngineService.mockMany();return "ok";}
}

6、TestController


package cn.jt.thermalapidemonstrationtdengine.controller;import cn.hutool.core.util.RandomUtil;
import cn.jt.thermalapidemonstrationtdengine.entity.A;
import cn.jt.thermalapidemonstrationtdengine.service.AService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {@Autowiredprivate AService aService;@GetMapping("/mockMysql")public String mockMysql() {A a = new A();a.setName(RandomUtil.randomString(5));aService.save(a);return "ok";}@Transactional(rollbackFor = Exception.class)@GetMapping("/mockMysql2")public String mockMysql2() {A a = new A();a.setName(RandomUtil.randomString(5));aService.save(a);int i = 1/0;A a2 = new A();a2.setName(RandomUtil.randomString(5));aService.save(a2);return "ok";}}

7、A

package cn.jt.thermalapidemonstrationtdengine.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@TableName("a")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class A {@TableId(type = IdType.AUTO)private Integer id;private String name;
}

8、AMapper

package cn.jt.thermalapidemonstrationtdengine.mapper;import cn.jt.thermalapidemonstrationtdengine.entity.A;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@Mapper
public interface AMapper extends BaseMapper<A> {
}

9、TdEngineService

package cn.jt.thermalapidemonstrationtdengine.service;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月31日*/
public interface TdEngineService {/*** 插入一条数据** @param ts 时间*/void insertOneByTime(java.sql.Timestamp ts);/*** 模拟大量数据*/void mockMany();
}

10、TdEngineServiceImpl

package cn.jt.thermalapidemonstrationtdengine.service.impl;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.jt.thermalapidemonstrationtdengine.config.DataSourceConfig;
import cn.jt.thermalapidemonstrationtdengine.service.TdEngineService;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.taosdata.jdbc.TSDBPreparedStatement;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;import javax.sql.DataSource;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月31日*/
@Slf4j
@Service
public class TdEngineServiceImpl implements TdEngineService {public static final ExecutorService MESSAGE_LISTENER_HANDLER_EXECUTOR =new ThreadPoolExecutor(20, 20,Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),new ThreadFactoryBuilder().setNameFormat("message-listener-handler-executor-" + "%d").setUncaughtExceptionHandler((thread, throwable) -> log.error("ThreadPool {} got exception", thread, throwable)).build(), new ThreadPoolExecutor.AbortPolicy());@Autowired@Qualifier(DataSourceConfig.TDENGINE_DATA_SOURCE)private DataSource dataSource;@Overridepublic void insertOneByTime(java.sql.Timestamp ts) {BigDecimal min = BigDecimal.valueOf(0.00);BigDecimal max = BigDecimal.valueOf(100.00);try (Connection conn = dataSource.getConnection()) {String psql = "INSERT INTO bfh.dn_bfh202203450556 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";try (TSDBPreparedStatement pstmt = conn.prepareStatement(psql).unwrap(TSDBPreparedStatement.class)) {// 如果是使用  parameterIndex 参数的方法,参数下标从1开始...pstmt.setTimestamp(1, ts);// 一共20个属性,20个通道for (int i = 2; i <= 21; i++) {float v = RandomUtil.randomBigDecimal(min, max).floatValue();pstmt.setFloat(i, v);}int i = pstmt.executeUpdate();log.info("插入成功:" + i);}} catch (SQLException e) {log.error("插入error ", e);}}@Overridepublic void mockMany() {// 三亿条数据int total = 300000000;
//        int total = 8 * 10000;// 前电脑CPU是8核的(给10个线程数量容易凑成整数)int threadCount = 10;// 每个线程需要处理的数量int perThread = total / threadCount;// 所有的任务列表List<CompletableFuture<Void>> allTaskList = new ArrayList<>();// 起始时间  2022-xx-01 15:30:50.123 ~ArrayList<Long> startTimeList = new ArrayList<>(threadCount);// 因为数据库设置了过期时间,是3650 即10年,所以插入的数据不能超过这个范围for (int i = 0; i < threadCount; i++) {LocalDateTime dateTime1 = LocalDateTime.of(2022, i + 1, 1, 15, 30, 50, 123_000_000);// 转换为毫秒startTimeList.add(dateTime1.toInstant(ZoneOffset.UTC).toEpochMilli());}String[] device_numbers = {"dn_bfh202203450556", "dn_bfh202307291045"};log.info("开始:{}", DateUtil.now());for (int i = 0; i < threadCount; i++) {int finalI = i;CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {// 每个线程每次1000条 数据int perSize = 1000;int perCount = perThread / perSize;int timeCount = 0;try (Connection conn = dataSource.getConnection()) {BigDecimal min = BigDecimal.valueOf(0.00);BigDecimal max = BigDecimal.valueOf(100.00);String psql = "INSERT INTO ? VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";try (TSDBPreparedStatement pstmt = conn.prepareStatement(psql).unwrap(TSDBPreparedStatement.class)) {for (int j = 1; j <= perCount; j++) {Long curThreadStartTime = startTimeList.get(finalI) + (++timeCount);pstmt.setTableName("bfh." + device_numbers[0]);ArrayList<Long> tsList = new ArrayList<>();for (int k = 0; k < perSize; k++, timeCount++) {tsList.add(curThreadStartTime + k);}pstmt.setTimestamp(0, tsList);// 一共20个属性,20个通道for (int p = 1; p <= 20; p++) {ArrayList<Float> fieldList = new ArrayList<>();for (int p1 = 0; p1 < perSize; p1++) {fieldList.add(RandomUtil.randomBigDecimal(min, max).floatValue());}pstmt.setFloat(p, fieldList);}// add columnpstmt.columnDataAddBatch();// execute columnpstmt.columnDataExecuteBatch();}}} catch (SQLException e) {throw new RuntimeException(e);}});allTaskList.add(voidCompletableFuture);}// 等待所有任务完成CompletableFuture<Void> allTasks = CompletableFuture.allOf(allTaskList.toArray(new CompletableFuture[allTaskList.size()]));// 阻塞等待所有任务完成allTasks.join();log.info("结束:{}", DateUtil.now());}
}

11、AService

package cn.jt.thermalapidemonstrationtdengine.service;import cn.jt.thermalapidemonstrationtdengine.entity.A;
import com.baomidou.mybatisplus.extension.service.IService;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
public interface AService extends IService<A> {
}

12、AServiceImpl

package cn.jt.thermalapidemonstrationtdengine.service.impl;import cn.jt.thermalapidemonstrationtdengine.entity.A;
import cn.jt.thermalapidemonstrationtdengine.mapper.AMapper;
import cn.jt.thermalapidemonstrationtdengine.service.AService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@Service
public class AServiceImpl extends ServiceImpl<AMapper, A> implements AService {
}

13、ThermalApiDemonstrationTdengineApplication

package cn.jt.thermalapidemonstrationtdengine;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan({"cn.jt.thermalapidemonstrationtdengine.mapper"})
public class ThermalApiDemonstrationTdengineApplication {public static void main(String[] args) {SpringApplication.run(ThermalApiDemonstrationTdengineApplication.class, args);}}

14、A.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.jt.thermalapidemonstrationtdengine.mapper.AMapper"></mapper>

15、Druid 数据源展示面板 http://127.0.0.1:7280/tadt/druid/index.html,有两个我们命名的数据源,有各自不同的配置,如果你没有展示,记得,先分别使用 两个数据源操作一下,比如风别请求MockOne 接口,都用到两个数据源,下面就会开始显示了。

在这里插入图片描述

3.1.2、初步感受TDengine的效率(作为粗略参考)

1、官网也给了一个 taosBenchmark,测试工具,大家可以去感受一下

在这里插入图片描述

2、这里要说的是,我们自己写代码感受一下,这边模拟一个设备的温度数据,这个设备一秒10条温度记录,这边模拟它一年的量,三亿条。

3、当然这边这是初略的感受一下,服务器设备(TDengine linux centos7.9)信息如下
在这里插入图片描述

6、生成数据的window电脑

在这里插入图片描述
在这里插入图片描述
7、请求上述接口mockMany,会启动10个线程,每个线程三千万条数据,每次批量插入1000条,这里使用官网的建议

  • 使用批量插入
  • 使用参数绑定

8、这边测试结果就是 开始:2023-08-02 09:02:10 ~ 结束:2023-08-02 09:37:01,其中我还随机操作了MySQL,一共耗时35分钟

  • 注意插入的时间ts字段不能重复,不然会执行数据覆盖的
  • 其次如果数据被大量相同时间戳的数据覆盖过,导致碎片多,数据导出再导入就行了(测试阶段可以直接删除库和表重新建立)。企业版支持在线重组。否则插入会很慢
  • 数据插入乱序的问题,虽然TDengine 会处理,将乱序的数据整理为顺序,但是这样会影响性能
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

9、这里补充上数据库,超级表,和子表的结构,如下

-- 创建一个数据库 保存最近十年的数据
-- KEEP 表示数据文件保存的天数,缺省值为 3650,取值范围 [1, 365000],且必须大于或等于 DURATION 参数值。
-- 数据库会自动删除保存时间超过 KEEP 值的数据。KEEP 可以使用加单位的表示形式,如 KEEP 100h、KEEP 10d 等,支持 m(分钟)、h(小时)和 d(天)三个单位。也可以不写单位,如 KEEP 50,此时默认单位为天
create database bfh KEEP 3650
use bfh
-- 10万个 设备就是 10万个 子表(一个设备一个子表),并且这个超级表下的都是同一种类型的采集量(比如温度)
-- 标签后续可以增加和修改,标签可以用于后续聚合,比如你增加一个 localtion的标签,后续你可以根据地理位置来聚合数据
-- 这里采用多列模型,因为这些通道的温度一定是同时采集的,如果有不同采集频率的可以使用单列模型,就是再建立一个超级表单独处理
-- dn 是 device_number 缩写CREATE STABLE if not exists bfh_temperature(ts timestamp,temperature1 float,temperature2 float,temperature3 float,temperature4 float,temperature5 float,temperature6 float,temperature7 float,temperature8 float,temperature9 float,temperature10 float,temperature11 float,temperature12 float,temperature13 float,temperature14 float,temperature15 float,temperature16 float,temperature17 float,temperature18 float,temperature19 float,temperature20 float) tags ( dn nchar(64))-- 创建子表的时候,建议将设备唯一标识作为表名称,如设备序列号(就是出厂的时候,硬件那边肯定会有的)-- dn 是 device_number 缩写,bfh202307291045是这台设备的型号
create table dn_bfh202307291045 using bfh_temperature tags('bfh202307291045')
create table dn_bfh202203450556 using bfh_temperature tags('bfh202203450556')           --  时间顺序可以乱序,tdengine 会在插入的时候,帮你排好顺序,所以我们可以放心的使用多线程插入
insert into bfh.dn_bfh202307291045(ts,temperature1)  VALUES ('2023-07-29 17:02:45.023',512.20)
--DELETE from bfh.dn_bfh202307291045select count(*) from bfh.dn_bfh202307291045--DROP DATABASE bfh;select count(*) from bfh_temperature;
select count(*) from dn_bfh202203450556;select * from dn_bfh202203450556 limit  1000000,10;
select * from dn_bfh202203450556 where ts>'2022-01-01 23:47:31.124' limit  1000000,10;

在这里插入图片描述
10、对数据库的数据进行查询操作

  • select count(*) from dn_bfh202203450556; 耗时91ms
    在这里插入图片描述
  • select count(*) from bfh_temperature; 耗时75ms

在这里插入图片描述

  • select * from dn_bfh202203450556 limit 1000000,10; 耗时373ms

在这里插入图片描述

  • select * from dn_bfh202203450556 where ts>‘2022-01-01 23:47:31.124’ limit 1000000,10; 耗时360ms

在这里插入图片描述

11、存盘存储大小,约莫23G

在这里插入图片描述
12、CPU 和内存使用

在这里插入图片描述

在这里插入图片描述

3.1.3、题外说明

1、因为我们给Mysql的数据源增加了 @Primary 注解,所以mybatisPlus 默认管理的是这个

2、如果后期还需要配置另外多个数据源,可以按照现在的方式去配置Three,four,等等数据源,然后再配置jdbcTemplate,再给jdbcTemplate配置事务,我们后面可以使用jdbcTemplate来操作数据库就行啦

  • 在 Spring Boot 中,JdbcTemplate 是 Spring 提供的用于执行 SQL 操作的简单 JDBC 工具。如果你在应用程序中使用了多数据源,并且希望为每个数据源分别配置 JdbcTemplate,可以像下面这样实现:
  • 在配置类中创建多个 JdbcTemplate Bean,分别关联到各自的数据源。
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;import javax.sql.DataSource;@Configuration
public class JdbcTemplateConfig {@Beanpublic JdbcTemplate jdbcTemplateOne(@Qualifier("dataSourceOne") DataSource dataSourceOne) {return new JdbcTemplate(dataSourceOne);}@Beanpublic JdbcTemplate jdbcTemplateTwo(@Qualifier("dataSourceTwo") DataSource dataSourceTwo) {return new JdbcTemplate(dataSourceTwo);}// 添加其他数据源的 JdbcTemplate Bean,根据需要继续添加
}
  • 在需要使用 JdbcTemplate 的 Service 或 Repository 类中,使用 @Autowired 注解注入对应的 JdbcTemplate
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;@Repository
public class MyRepository {private final JdbcTemplate jdbcTemplateOne;private final JdbcTemplate jdbcTemplateTwo;@Autowiredpublic MyRepository(@Qualifier("jdbcTemplateOne") JdbcTemplate jdbcTemplateOne,@Qualifier("jdbcTemplateTwo") JdbcTemplate jdbcTemplateTwo) {this.jdbcTemplateOne = jdbcTemplateOne;this.jdbcTemplateTwo = jdbcTemplateTwo;}// 在这里可以使用 jdbcTemplateOne 和 jdbcTemplateTwo 分别对不同数据源进行数据库操作// ...
}
  • 在 Spring Boot 中,JdbcTemplate 默认是不支持事务的。如果你需要在使用 JdbcTemplate 的方法中启用事务支持,你可以使用 Spring 的事务管理机制,即声明式事务。 声明式事务允许你使用注解来标记需要进行事务管理的方法,Spring 将会自动为这些方法添加事务处理。在方法执行过程中,如果发生异常,事务会被回滚;如果方法执行成功,事务会被提交。
  • 以下是在 JdbcTemplate 方法中启用事务支持的步骤:配置事务管理器:首先,你需要在配置类中配置事务管理器,以便在应用程序中启用事务支持。
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.sql.DataSource;@Configuration
@EnableTransactionManagement
public class TransactionConfig {@Beanpublic PlatformTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}
  • 在上面的代码中,我们创建了一个名为 transactionManager 的事务管理器 Bean,并将 dynamicDataSource 数据源传递给它。dynamicDataSource 是我们前面配置的动态数据源,它包含了多个数据源。通过在配置类上添加 @EnableTransactionManagement 注解,我们启用了 Spring 的事务管理功能。
  • 在需要进行事务管理的 Service 或 Repository 类中,使用 @Transactional 注解标记需要启用事务的方法。
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class MyService {private final JdbcTemplate jdbcTemplate;public MyService(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}@Transactionalpublic void someTransactionalMethod() {// 在这个方法中执行数据库操作,该方法会启用事务// 如果方法执行过程中出现异常,事务会回滚// 如果方法执行成功,事务会提交}
}
  • 通过在 someTransactionalMethod() 方法上添加 @Transactional 注解,我们标记该方法需要进行事务管理。这样,在调用该方法时,Spring 将会自动为该方法添加事务支持,确保数据库操作的原子性和一致性。

  • 注意,为了使 @Transactional 注解生效,必须确保该注解是由 Spring 托管的 Bean 方法上使用。如果你在同一个类中的非 public 方法上使用该注解,Spring 将无法应用事务代理。

  • 通过上述配置,可以在使用 JdbcTemplate 的方法中启用事务支持,并确保数据库操作的事务安全性。

3.2、使用mybatisPlus 和 dynamic-datasource-spring-boot-starter 来管理

1、项目结构如下,项目也很简单,大家看代码就能明白了

在这里插入图片描述

3.2.1、项目配置及其代码

1、pom.xml

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.14</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.26</version></dependency><!-- mybatis plus 依赖 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><!--  mybatis plus 动态数据源 多数据源 --><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.1</version></dependency><!--Druid  方便控制台查看--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency><!-- lombok插件 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- alibaba的fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.79</version></dependency><!-- hutool工具 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.5</version></dependency><!-- TDengine 依赖 --><dependency><groupId>com.taosdata.jdbc</groupId><artifactId>taos-jdbcdriver</artifactId><version>3.2.4</version></dependency></dependencies>

2、application.yml

server:port: 7012shutdown: gracefulspring:lifecycle:timeout-per-shutdown-phase: 30sautoconfigure:# DruidDataSourceAutoConfigure会注入一个DataSourceWrapper,其会在原生的spring.datasource下找url,# username,password等。动态数据源URL等配置是在dynamic下,因此需要排除,否则会报错。# 或者 在启动类上排除 @SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure# 配置数据源信息datasource:druid: # 参数说明 https://developer.aliyun.com/article/1157595filters: stat,wall  #监控统计(包含慢日志)用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall (这个必须是全局配置,所有数据源的方式是一样的,你放到具体的数据源下面配置,会发现启动报错)web-stat-filter: # 不统计这些请求(这个必须是全局配置,所有数据源的方式是一样的,你放到具体的数据源下面配置,会发现没有这个参数)exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'#设置访问druid监控页面的拦截路径及账号和密码stat-view-servlet:allow: 127.0.0.1  # 允许那些ip 访问login-username: adminlogin-password: 123456enabled: true # 开启网页访问  http://127.0.0.1:7012/druid/sql.htmldynamic:# 设置默认的数据源或者数据源组,默认值即为masterprimary: test01# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源strict: falsedatasource:test01:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/test01?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8username: rootpassword: 123456druid: # 单独配置validation-query: SELECT 1 FROM DUAL  # 连接是否有效的查询语句validation-query-timeout: 60000# 建议 连接数 = ((核心数 * 2) + 有效磁盘数)initial-size: 50  #初始化时建立物理连接的个数,初始化发生在显示调用 init 方法,或者第一次 getConnection 时min-idle: 50  # 最小连接池数量max-active: 100  #最大连接池数量test-on-borrow: false  #申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetest-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetime-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒min-evictable-idle-time-millis: 300000  # 配置一个连接在池中最小生存的时间,单位是毫秒test02:url: jdbc:mysql://127.0.0.1:3306/test02?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456druid: # 单独配置validation-query: SELECT 1 FROM DUAL  # 连接是否有效的查询语句validation-query-timeout: 60000 # 连接是否有效的查询超时时间# 建议 连接数 = ((核心数 * 2) + 有效磁盘数)initial-size: 40  #初始化时建立物理连接的个数,初始化发生在显示调用 init 方法,或者第一次 getConnection 时min-idle: 40  # 最小连接池数量max-active: 100  #最大连接池数量test-on-borrow: false  #申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetest-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetime-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒min-evictable-idle-time-millis: 300000  # 配置一个连接在池中最小生存的时间,单位是毫秒thermalDemonstration: # 配置 tdengine 的数据源driver-class-name: com.taosdata.jdbc.TSDBDriver# 这里指定了具体的数据库 需要注意,# 如果换成不指定具体数据库名称 jdbc:TAOS://192.168.172.129:6030?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8 则在sql中使用必须要指定数据库的名称 dba.table_burl: jdbc:TAOS://192.168.172.129:6030/mp_test?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8username: rootpassword: taosdatadruid: # 单独配置validation-query: select server_status()  # 连接是否有效的查询语句validation-query-timeout: 60000 # 连接是否有效的查询超时时间# 建议 连接数 = ((核心数 * 2) + 有效磁盘数)initial-size: 10  #初始化时建立物理连接的个数,初始化发生在显示调用 init 方法,或者第一次 getConnection 时min-idle: 10  # 最小连接池数量max-active: 10  #最大连接池数量test-on-borrow: false  #申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetest-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为truetime-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒min-evictable-idle-time-millis: 300000  # 配置一个连接在池中最小生存的时间,单位是毫秒# mybatis配置
mybatis-plus:# xml文件路径mapper-locations: classpath:mapper/*/*.xml# 实体类路径type-aliases-package: cn.gxm.springboottdengine.entity.*configuration:# 驼峰转换map-underscore-to-camel-case: true# 是否开启缓存cache-enabled: false# 打印sqllog-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 全局配置global-config:# 数据库字段驼峰下划线转换db-column-underline: true# id自增类型(数据库id自增)id-type: 0

3、TDengineTemperatureController

package cn.gxm.springboottdengine.controller;import cn.gxm.springboottdengine.entity.thermaldemonstration.Temperature;
import cn.gxm.springboottdengine.mapper.thermaldemonstration.TemperatureMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@RestController
@RequestMapping("/td/temperature")
public class TDengineTemperatureController {private static Random random = new Random(System.currentTimeMillis());private static String[] locations = {"北京", "上海", "深圳", "广州", "杭州"};@Autowiredprivate TemperatureMapper mapper;@GetMapping("before")public String before() {mapper.dropSuperTable();// create table temperaturemapper.createSuperTable();// create table t_X using temperaturefor (int i = 0; i < 10; i++) {mapper.createTable("t" + i, locations[random.nextInt(locations.length)], i);}// insert into tableint affectRows = 0;// insert 10 tablesfor (int i = 0; i < 10; i++) {// each table insert 5 rowsfor (int j = 0; j < 5; j++) {Temperature one = new Temperature();one.setTs(new Timestamp(1605024000000L));one.setTemperature(random.nextFloat() * 50);one.setLocation("望京");one.setTbIndex(i);affectRows += mapper.insertOne(one);}}
//        Assert.assertEquals(50, affectRows);return "影响数量 : " + affectRows;}@GetMapping("after")public String after() {mapper.dropSuperTable();return "删除超级表";}/**** test SelectList* **/@GetMapping("testSelectList")public List<Temperature> testSelectList() {List<Temperature> temperatureList = mapper.selectList(null);
//        temperatureList.forEach(System.out::println);return temperatureList;}/**** test InsertOne which is a custom metheod* ***/@GetMapping("testInsert")public String testInsert() {Temperature one = new Temperature();one.setTs(new Timestamp(1605025000000L));one.setTemperature(random.nextFloat() * 50);one.setLocation("望京");int affectRows = mapper.insertOne(one);
//        Assert.assertEquals(1, affectRows);return "插入行数:" + affectRows;}/**** test  select By map* ***/@GetMapping("testSelectByMap")public List<Temperature> testSelectByMap() {Map<String, Object> map = new HashMap<>();map.put("location", "北京");List<Temperature> temperatures = mapper.selectByMap(map);
//        Assert.assertTrue(temperatures.size() > 1);return temperatures;}/**** test selectObjs* **/@GetMapping("testSelectObjs")public List<Object> testSelectObjs() {List<Object> ts = mapper.selectObjs(null);
//        System.out.println(ts);return ts;}/*** test selectC ount**/@GetMapping("testSelectCount")public long testSelectCount() {long count = mapper.selectCount(null);
//        Assert.assertEquals(10, count);return count;}/***** 分页*/@GetMapping("testSelectPage")public IPage<Temperature> testSelectPage() {IPage page = new Page(1, 2);IPage<Temperature> temperatureIPage = mapper.selectPage(page, null);System.out.println("total : " + temperatureIPage.getTotal());System.out.println("pages : " + temperatureIPage.getPages());for (Temperature temperature : temperatureIPage.getRecords()) {System.out.println(temperature);}return temperatureIPage;}}

4、TDengineWeatherController

package cn.gxm.springboottdengine.controller;import cn.gxm.springboottdengine.entity.thermaldemonstration.Weather;
import cn.gxm.springboottdengine.mapper.thermaldemonstration.WeatherMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.sql.Timestamp;
import java.util.List;
import java.util.Random;@RestController
@RequestMapping("/td/weather")
public class TDengineWeatherController {private static Random random = new Random(System.currentTimeMillis());@Autowiredprivate WeatherMapper mapper;@GetMapping("createTable")public void createTable() {mapper.dropTable();mapper.createTable();Weather one = new Weather();one.setTs(new Timestamp(1605024000000L));one.setTemperature(12.22f);one.setLocation("望京");one.setHumidity(100);mapper.insertOne(one);}@GetMapping("testSelectList")public List<Weather> testSelectList() {List<Weather> weathers = mapper.selectList(null);
//        weathers.forEach(System.out::println);return weathers;}@GetMapping("testInsert")public int testInsert() {Weather one = new Weather();one.setTs(new Timestamp(1605024000000L));one.setTemperature(random.nextFloat() * 50);one.setHumidity(random.nextInt(100));one.setLocation("望京");int affectRows = mapper.insert(one);
//        Assert.assertEquals(1, affectRows);return affectRows;}//    @Test@GetMapping("testSelectOne")public Weather testSelectOne() {QueryWrapper<Weather> wrapper = new QueryWrapper<>();wrapper.eq("location", "望京");Weather one = mapper.selectOne(wrapper);
//        System.out.println(one);
//        Assert.assertEquals(12.22f, one.getTemperature(), 0.00f);
//        Assert.assertEquals("望京", one.getLocation());return one;}// @Test// public void testSelectByMap() {//     Map<String, Object> map = new HashMap<>();//     map.put("location", "beijing");//     List<Weather> weathers = mapper.selectByMap(map);//     Assert.assertEquals(1, weathers.size());// }@GetMapping("testSelectObjs")public List<Object> testSelectObjs() {List<Object> ts = mapper.selectObjs(null);
//        System.out.println(ts);return ts;}@GetMapping("testSelectCount")public long testSelectCount() {long count = mapper.selectCount(null);
//        Assert.assertEquals(5, count);
//        System.out.println(count);return count;}@GetMapping("testSelectPage")public IPage<Weather> testSelectPage() {IPage page = new Page(1, 2);IPage<Weather> weatherIPage = mapper.selectPage(page, null);
//        System.out.println("total : " + weatherIPage.getTotal());
//        System.out.println("pages : " + weatherIPage.getPages());
//        for (Weather weather : weatherIPage.getRecords()) {
//            System.out.println(weather);
//        }return weatherIPage;}}

5、TestController

package cn.gxm.springboottdengine.controller;import cn.gxm.springboottdengine.entity.test01.A;
import cn.gxm.springboottdengine.entity.test02.B;
import cn.gxm.springboottdengine.service.test01.AService;
import cn.gxm.springboottdengine.service.test02.BService;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@RestController
@RequestMapping("/test")
public class TestController {@Autowiredprivate AService aService;@Autowiredprivate BService bService;@GetMapping("/mockMysql")
//    @Transactional(rollbackFor = Exception.class) 得用 @DSTransactionalpublic String mockMysql() {A a = new A();a.setName(RandomUtil.randomString(5));aService.save(a);B b = new B();b.setAge(RandomUtil.randomInt(1, 200));bService.save(b);return "ok";}@GetMapping("/aTrans")@DSTransactionalpublic String aTrans() {A a = new A();a.setName(RandomUtil.randomString(5));aService.save(a);int i = 1 / 0;A a2 = new A();a2.setName(RandomUtil.randomString(5));aService.save(a2);return "ok";}@GetMapping("/bTrans")@DSTransactionalpublic String bTrans() {B b = new B();b.setAge(RandomUtil.randomInt(1, 200));bService.save(b);int i = 1 / 0;B b2 = new B();b2.setAge(RandomUtil.randomInt(1, 200));bService.save(b2);return "ok";}
}

6、A

package cn.gxm.springboottdengine.entity.test01;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@TableName("a")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class A {@TableId(type = IdType.AUTO)private Integer id;private String name;
}

7、B

package cn.gxm.springboottdengine.entity.test02;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@TableName("b")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class B {@TableId(type = IdType.AUTO)private Integer id;private Integer age;
}

8、Temperature

package cn.gxm.springboottdengine.entity.thermaldemonstration;import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;import java.sql.Timestamp;@Data
public class Temperature {private Timestamp ts;private float temperature;private String location;@TableField("tbindex")private int tbIndex;}

9、Weather

package cn.gxm.springboottdengine.entity.thermaldemonstration;import lombok.Data;import java.sql.Timestamp;@Data
public class Weather {private Timestamp ts;private float temperature;private int humidity;private String location;}

10、AMapper

package cn.gxm.springboottdengine.mapper.test01;import cn.gxm.springboottdengine.entity.test01.A;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@Mapper
@DS("test01")
public interface AMapper extends BaseMapper<A> {
}

11、BMapper

package cn.gxm.springboottdengine.mapper.test02;import cn.gxm.springboottdengine.entity.test02.B;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@Mapper
@DS("test02")
public interface BMapper extends BaseMapper<B> {
}

12、TemperatureMapper

package cn.gxm.springboottdengine.mapper.thermaldemonstration;import cn.gxm.springboottdengine.entity.thermaldemonstration.Temperature;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;@Mapper
@DS("thermalDemonstration")
public interface TemperatureMapper extends BaseMapper<Temperature> {// 演示放到 xml 中调用
//    @Update("CREATE TABLE if not exists temperature(ts timestamp, temperature float) tags(location nchar(64), tbIndex int)")int createSuperTable();@Update("create table #{tbName} using temperature tags( #{location}, #{tbindex})")int createTable(@Param("tbName") String tbName, @Param("location") String location, @Param("tbindex") int tbindex);@Update("drop table if exists temperature")void dropSuperTable();@Insert("insert into t${tbIndex}(ts, temperature) values(#{ts}, #{temperature})")int insertOne(Temperature one);}

13、WeatherMapper

package cn.gxm.springboottdengine.mapper.thermaldemonstration;import cn.gxm.springboottdengine.entity.thermaldemonstration.Weather;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;@Mapper
@DS("thermalDemonstration")
public interface WeatherMapper extends BaseMapper<Weather> {// 演示放到 xml 中调用
//    @Update("CREATE TABLE if not exists weather(ts timestamp, temperature float, humidity int, location nchar(100))")int createTable();@Insert("insert into weather (ts, temperature, humidity, location) values(#{ts}, #{temperature}, #{humidity}, #{location})")int insertOne(Weather one);@Update("drop table if exists weather")void dropTable();
}

14、AServiceImpl

package cn.gxm.springboottdengine.service.test01.impl;import cn.gxm.springboottdengine.entity.test01.A;
import cn.gxm.springboottdengine.mapper.test01.AMapper;
import cn.gxm.springboottdengine.service.test01.AService;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@Service
public class AServiceImpl extends ServiceImpl<AMapper, A> implements AService {
}

15、AService

package cn.gxm.springboottdengine.service.test01;import cn.gxm.springboottdengine.entity.test01.A;
import com.baomidou.mybatisplus.extension.service.IService;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
public interface AService extends IService<A> {
}

16、BServiceImpl

package cn.gxm.springboottdengine.service.test02.impl;import cn.gxm.springboottdengine.entity.test02.B;
import cn.gxm.springboottdengine.mapper.test02.BMapper;
import cn.gxm.springboottdengine.service.test02.BService;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
@Service
public class BServiceImpl extends ServiceImpl<BMapper, B> implements BService {
}

17、

package cn.gxm.springboottdengine.service.test02;import cn.gxm.springboottdengine.entity.test02.B;
import com.baomidou.mybatisplus.extension.service.IService;/*** @author GXM* @version 1.0.0* @Description TODO* @createTime 2023年07月28日*/
public interface BService extends IService<B> {
}

18、SpringbootTdengineDynamicDataSourceApplication

package cn.gxm.springboottdengine;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@MapperScan({"cn.gxm.springboottdengine.mapper"})
public class SpringbootTdengineDynamicDataSourceApplication {public static void main(String[] args) {SpringApplication.run(SpringbootTdengineDynamicDataSourceApplication.class, args);}}

19、A.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.gxm.springboottdengine.mapper.test01.AMapper"></mapper>

20、B.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.gxm.springboottdengine.mapper.test02.BMapper"></mapper>

21、TemperatureMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.gxm.springboottdengine.mapper.thermaldemonstration.TemperatureMapper"><update id="createSuperTable">CREATE TABLE if not exists temperature(tstimestamp,temperaturefloat) tags(location nchar(64), tbIndex int)</update>
</mapper>

22、WeatherMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.gxm.springboottdengine.mapper.thermaldemonstration.WeatherMapper"><update id="createTable">CREATE TABLE if not exists weather(tstimestamp,temperaturefloat,humidityint,locationnchar(100))</update>
</mapper>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/19892.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

K8s影响Pod调度和Deployment

5.应用升级回滚和弹性伸缩

数学学习——最优化问题引入、凸集、凸函数、凸优化、梯度、Jacobi矩阵、Hessian矩阵

文章目录 最优化问题引入凸集凸函数凸优化梯度Jacobi矩阵Hessian矩阵 最优化问题引入 例如&#xff1a;有一根绳子&#xff0c;长度一定的情况下&#xff0c;需要如何围成一个面积最大的图像&#xff1f;这就是一个最优化的问题。就是我们高中数学中最常见的最值问题。 最优化…

【深度学习】Transformer,Self-Attention,Multi-Head Attention

必读文章&#xff1a; https://blog.csdn.net/qq_37541097/article/details/117691873 论文名&#xff1a;Attention Is All You Need 文章目录 1、Self-Attention 自注意力机制2、Multi-Head Attention 1、Self-Attention 自注意力机制 Query&#xff08;Q&#xff09;表示当…

企业服务器中了Locked勒索病毒后怎么办,如何解决问题并提高防范意识

科学技术的发展给我们的生活带来了极大便利&#xff0c;但也为企业带来了安全威胁。近期&#xff0c;我们收到很多企业的求助&#xff0c;企业的服务器中了locked后缀勒索病毒&#xff0c;计算机上的所有文件都被加密&#xff0c;无法被正常调取&#xff0c;严重影响了企业的正…

KepwareEX配置API REST接口

服务端Kepware设置 API允许连接设置 创建通道 请求地址(POST)&#xff1a; https://<主机名_或_ip>:<端口>/config/v1/project/channels 以下示例使用postman工具访问API创建了一个名为Channel1 的通道&#xff0c;其使用在本地主机运行的服务器中的Simulator …

UE5 c++ 的文件操作(记录备忘)

函数库.h // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "Microsoft/AllowMicrosoftPlatformTypes.h" #incl…

在 React 中渲染大型数据集的 3 种方法

随着 Web 应用程序变得越来越复杂&#xff0c;我们需要找到有效的方法来优化性能和渲染大型数据集。在 React 应用程序中处理大型数据集时&#xff0c;一次呈现所有数据可能会导致性能不佳和加载时间变慢。 虚拟化是一种通过一次仅呈现数据集的一部分来解决此问题的技术&#…

1. C++面向过程

一、C简介 1.1 C的产生及其特点 从C语言发展演变而来&#xff0c;解决了C语言中存在的一些问题&#xff0c;并增加了对面向对象程序设计方法的支持 与其他高级语言相比&#xff0c;C语言可以直接访问物理地址&#xff1b;与汇编相比它具有良好的可读性和可移植性 C于1980年由…

【MySQL】使用C/C++连接MySQL数据库

【MySQL】使用C/C连接MySQL数据库 验证使用select特殊点 本文目的&#xff1a;使用MySQL提供的CAPI完成对数据库的操作 验证 #include <iostream> #include <mysql/mysql.h>int main() {std::cout<<"mysql cilent version: "<<mysql_get_cl…

uniapp h5支付宝支付后端返回Form表单,前端如何处理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1.调取接口拿到后端返回的form表单 前言 uniapp h5 支付宝支付&#xff0c;后端返回一串form表单&#xff0c;前端如何拿到支付串并且调用支付 1.调取接口拿到…

面试热题(接雨水问题)

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 我们看到题的第一步&#xff0c;永远是对入参进行判断 public int trap(int[] height) {if (height null) {return 0;}...} 但是我们想想看&#xff0c;接…

【数据结构】单链表

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;数据结构 &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、什么是链表 1.1链表的概念及结构 1.2单链表的结构 二、链表的实现 …

macOS install redis遇到的bug(tar包,homebrew安装,守护进程redis.conf配置)

官网下载tar包再make install 首先是sudo make test的时候一直报 !!! WARNING The following tests failed: *** [err]: trim on SET with big value in tests/unit/type/string.tcl Expected [r memory usage key] < 42000 (context: type source line 478 file /usr/loca…

Qt项目---简单的计算器

在这篇技术博客中&#xff0c;我们将介绍如何使用Qt框架实现一个简单的计算器应用。我们将使用C编程语言和Qt的图形用户界面库来开发这个应用&#xff0c;并展示如何实现基本的算术操作。 项目设置 首先&#xff0c;我们需要在Qt Creator中创建一个新的Qt Widgets应用程序项目…

使用 github 同步谷歌浏览器书签

想必使用谷歌浏览器Chrome的用户一定非常头疼的一件事就是&#xff1a;账户不能登录&#xff0c;书签收藏夹不能同步&#xff0c;换一台电脑书签收藏夹没有了&#xff01; 下面教大家一招亲测有效适用的方法解决书签同步问题&#xff0c;在任何电脑都可以同步了 1、去下载谷歌…

【Linux】——线程安全

目录 关于线程进程的问题 可重入与线程安全 常见的线程安全的情况 常见的不可重入的情况 常见的可重入的情况 可重入与线程安全区别 可重入与线程安全联系 Linux线程互斥 进程线程间的互斥相关概念 互斥量mutex 互斥量mutex常用接口 互斥量改造抢票系统 互斥量的原…

Zookeeper基础操作

搭建Zookeeper服务器 windows下部署 下载地址: https://mirrors.cloud.tencent.com/apache/zookeeper/zookeeper-3.7.1/ 修改配置文件 打开conf目录&#xff0c;将 zoo_sample.cfg复制一份&#xff0c;命名为 zoo.cfg打开 zoo.cfg&#xff0c;修改 dataDir路径&#xff0c…

leetcode 452. 用最少数量的箭引爆气球

2023.8.2 本题思路先将二维数组points按照第一个维度排序&#xff0c; 然后初始化射箭数为1&#xff0c;因为题中提示说了最少有一个气球。 在遍历这些气球&#xff0c;看是否有重叠&#xff0c;如果没有重叠区域&#xff0c;射箭数&#xff1b;如果有重叠区域&#xff0c;更新…

java快速生成数据库表文档(HTML、DOC、MD)

在企业级开发中、我们经常会有编写数据库表结构文档的时间付出&#xff0c;关于数据库表结构文档状态&#xff1a;要么没有、要么有、但都是手写、后期运维开发&#xff0c;需要手动进行维护到文档中&#xff0c;很是繁琐&#xff0c;这里推荐一个开源项目&#xff1a;screw gi…

Spring源码面试题

Spring源码面试题 谈谈你对Spring框架的理解? Spring 是一个开源的应用程序框架&#xff0c;它起源于 Rod Johnson 在其著名的 Spring Framework 专著中提出的一个轻量级框架的观念。下面是 Spring 的发展历史&#xff1a; 2002 年&#xff0c;Rod Johnson 发表了他的专著 …