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: 7280
  servlet:
    # 项目路径
    context-path: /tadt
  shutdown: graceful  #开启优雅停机

spring:
  application:
    name: thermal-api-demonstration-tdengine
  lifecycle:
    timeout-per-shutdown-phase: 30s #设置缓冲时间 默认也是30s
  # Mysql配置
  datasource:
    druid:
      # 接下来(one,two)其实就都是自定义配置了,springBoot是识别不了的(当然你也可以另起其它行,到其它位置),我们需要将这些配置映射到对应的类上,springBoot不会帮我们做
      one:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/test01?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8
        username: root
        password: 123456
        type: com.alibaba.druid.pool.DruidDataSource
        name: 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检测连接是否有效,开启会降低性能,默认为true
        test-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
        time-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_b
        url: jdbc:TAOS://192.168.172.129:6030/mp_test?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8
        username: root
        password: taosdata
        type: com.alibaba.druid.pool.DruidDataSource
        name: 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检测连接是否有效,开启会降低性能,默认为true
        test-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
        time-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        min-evictable-idle-time-millis: 300000  # 配置一个连接在池中最小生存的时间,单位是毫秒

  # 热部署
  devtools:
    restart:
      enabled: true
  # jackson 配置
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
  # 上传文件
  servlet:
    multipart:
      max-file-size: 50MB
      max-request-size: 50MB

logstash:
  host: 192.168.172.228  # logstash部署的服务器IP
  env: dev  # 将日志加载到elk中项目的环境后缀名称-时间

# mybatis配置
mybatis-plus:
  # xml文件路径
  mapper-locations: classpath:mapper/*/*.xml
  # 实体类路径
  type-aliases-package: com.asurplus.*.entity
  configuration:
    # 驼峰转换
    map-underscore-to-camel-case: true
    # 是否开启缓存
    cache-enabled: false
    # 打印sql
    log-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
     */
    @Bean
    public 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
     */
    @Bean
    public 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 {

    @Autowired
    private 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 {

    @Autowired
    private 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;

    @Override
    public 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);
        }
    }

    @Override
    public 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 column
                            pstmt.columnDataAddBatch();
                            // execute column
                            pstmt.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_bfh202307291045

select 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 {

    @Bean
    public JdbcTemplate jdbcTemplateOne(@Qualifier("dataSourceOne") DataSource dataSourceOne) {
        return new JdbcTemplate(dataSourceOne);
    }

    @Bean
    public 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;

    @Autowired
    public 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 {

    @Bean
    public 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;
    }

    @Transactional
    public 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: 7012
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s
  autoconfigure:
    # 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/1157595
      filters: 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: admin
        login-password: 123456
        enabled: true # 开启网页访问  http://127.0.0.1:7012/druid/sql.html
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: test01
      # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        test01:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://127.0.0.1:3306/test01?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8
          username: root
          password: 123456
          druid: # 单独配置
            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检测连接是否有效,开启会降低性能,默认为true
            test-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
            time-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-8
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: 123456
          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检测连接是否有效,开启会降低性能,默认为true
            test-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
            time-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_b
          url: jdbc:TAOS://192.168.172.129:6030/mp_test?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8
          username: root
          password: taosdata
          druid: # 单独配置
            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检测连接是否有效,开启会降低性能,默认为true
            test-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
            time-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
    # 打印sql
    log-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 = {"北京", "上海", "深圳", "广州", "杭州"};


    @Autowired
    private TemperatureMapper mapper;

    @GetMapping("before")
    public String before() {
        mapper.dropSuperTable();
        // create table temperature
        mapper.createSuperTable();
        // create table t_X using temperature
        for (int i = 0; i < 10; i++) {
            mapper.createTable("t" + i, locations[random.nextInt(locations.length)], i);
        }
        // insert into table
        int affectRows = 0;
        // insert 10 tables
        for (int i = 0; i < 10; i++) {
            // each table insert 5 rows
            for (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());

    @Autowired
    private 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 {

    @Autowired
    private AService aService;
    @Autowired
    private BService bService;

    @GetMapping("/mockMysql")
//    @Transactional(rollbackFor = Exception.class) 得用 @DSTransactional
    public 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")
    @DSTransactional
    public 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")
    @DSTransactional
    public 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
        (
            ts
            timestamp,
            temperature
            float
        ) 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
        (
            ts
            timestamp,
            temperature
            float,
            humidity
            int,
            location
            nchar
        (
            100
        ))
    </update>
</mapper>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

django bootstrap html实现左右布局,带折叠按钮,左侧可折叠隐藏

一、实现的效果 在django项目中,需要使用bootstrap 实现一个左右分布的布局,左侧区域可以折叠隐藏起来,使得右侧的显示区域变大。(为了区分区域,左右加了配色,不好看的修改颜色即可) 点击折叠按钮,左侧区域隐藏,右侧区域铺满: 二、实现思路 1、使用col-md属性,让左…

SOC FPGA之流水灯设计

一、DS-5简介 Altera Soc EDS开发套件的核心是Altera版ARM Development Studio 5(DS-5)工具包&#xff0c;为SoC器件提供了完整的嵌入式开发环境、FPGA自适应调试和对Altera工具的兼容。 1.1 DS-5 eclipse破解 首先下载破解器 然后进入cmd运行&#xff0c;进入到破解器所在文…

【八】mybatis 日志模块设计

mybatis 日志模块设计 简介&#xff1a;闲来无事阅读一下mybatis的日志模块设计&#xff0c;学习一下优秀开源框架的设计思路&#xff0c;提升自己的编码能力 模块设计 在Mybatis内部定义了4个级别&#xff1a;Error:错误 、warn:警告、debug:调试、trance&#xff0c;日志优…

【计算机网络】应用层协议 -- DNS协议

文章目录 1. DNS背景2. 域名简介3. 域名解析过程4. 使用dig查看DNS过程 1. DNS背景 DNS&#xff08;Domain Name System&#xff0c;域名系统&#xff09;协议&#xff0c;是一个用来将域名转化为IP地址的应用层协议。 TCP/IP当中通过IP地址和端口号的方式&#xff0c;来确定…

C语言之结构体篇(简)

结构体 结构体的认知结构体的声明一般声明特殊声明匿名结构体类型 结构体自引用结构体变量的定义与初始化结构体变量的定义结构体变量的初始化 结构体传参结构体内存对齐位段位段声明位段的内存分配位段跨平台问题: 结构体是由我们自己创造的一种类型&#xff0c;使得C语言有能…

js-匈牙利算法

匈牙利算法 素数伴侣新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个注脚注释也是必不可少的K…

H5打包封装小程序系统开发

H5打包封装小程序系统开发 H5打包封装小程序系统开发是指将H5页面打包封装成小程序的开发过程。下面是一个简单的步骤&#xff1a; 准备工作&#xff1a;首先&#xff0c;需要准备好H5页面的代码和资源文件。确保H5页面在浏览器中正常运行&#xff0c;并且没有依赖于浏览器特…

『赠书活动 | 第十六期』《深入浅出Java虚拟机:JVM原理与实战》

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 『赠书活动 &#xff5c; 第十六期』 本期书籍&#xff1a;《深入浅出Java虚拟机&#xff1a;JVM原理与实战》 赠书规则&#xff1a;评论区&#xff1a;点赞&#xff…

将Visio和Excel导出成没有白边的PDF文件

1、VISIO如何无白边导出pdf格式 在使用Latex时&#xff0c;要导入矢量图eps格式。但是VISIO无法输出eps格式&#xff0c;这就需要将其导出为pdf。但是导出pdf时&#xff0c;往往会有大量的白边。VISIO无白边导出pdf格式的方法如下&#xff1a; 1.文件——开发工具——显示sha…

并发 如何创建线程 多线程

进程&#xff1a;一个程序的执行过程 线程&#xff1a;一个方法就是一个线程 并发&#xff1a;多个线程抢夺一个资源 操作同一个对象 创建线程方法1 //创建线程方法1 继承Thread类 重写润方法 调用start开启线程 public class TestThead extends Thread{Overridepublic voi…

redis 高级篇 redis 源码的读取分析

一 redis源码分析 1.1 源码分析 1每一个kv键值对应有一个dictEntry。 2.底层数据结构

景联文科技高质量成品数据集上新啦!

景联文科技近期上新多个成品数据集&#xff0c;包含图像、视频等多种类型的数据&#xff0c;涵盖丰富的场景&#xff0c;可满足不同模型的多元化需求。 高质量成品数据集可用于训练和优化模型&#xff0c;使得模型能够更加全面和精准地理解和处理任务&#xff0c;更好地应对复…

eclipse was unable to locate its companion shared library

当转移或者Copy工程时&#xff0c; eclipse was unable to locate its companion shared library eclipse.ini 里面的路径配置错误导致 --launcher.library C:/Users/**/.p2/pool/plugins/org.eclipse.equinox. launcher.win32.win32.x86_64_1.2.700.v20221108-1024 -product …

17. Spring 事务

目录 1. 事务定义 2. MySQL 中的事务使用 3. 没有事务时的插入 4. Spring 编程式事务 5. Spring 声明式事务 5.1 Transactional 作用范围 5.2 Transactional 参数说明 5.3 Transactional 工作原理 1. 事务定义 将⼀组操作封装成一个执行单元&#xff08;封装到一起…

人工智能安全-2-非平衡数据处理

0 提纲 现象与原因非平衡数据处理方法概览数据预处理层面特征层算法层面1 现象与原因 非平衡数据分类问题:在网络信息安全问题中,诸如恶意软件检测、SQL注入、不良信息检测等许多问题都可以归结为机器学习分类问题。这类机器学习应用问题中,普遍存在非平衡数据的现象。 产…

java系列之list集合分组

文章目录 前言一、list是什么&#xff1f;二、list集合分组总结 前言 在Java编程中&#xff0c;List集合是一种常用的数据结构&#xff0c;用于存储一组元素。有时候&#xff0c;我们需要对List集合中的元素进行分组操作&#xff0c;即将相同属性或特征的元素归类到一组。这种…

向 Maven 中央仓库上传一个修改过的基于jeecg的autoPOI的 jar包记录

1、注册https://issues.sonatype.org/账号 下面就代表注册好了&#xff0c;同时提交的工单也通过了 2、这里主要是goupId 需要进行认证&#xff0c;需要到域名注册商近一个txt的解析&#xff0c;以便确保这个是你的 通过下面来验证你的域名信息&#xff0c;这里主要是上面的工…

前端文件上传实践与后端处理——文件分块上传

文件上传是现代Web应用程序中常见的功能之一。在这篇博客中&#xff0c;我们将探讨一个简单但完整的前端文件上传实践&#xff0c;同时提供一个后端示例&#xff0c;演示如何处理上传的文件。我们将使用JavaScript作为前端语言&#xff0c;并结合Node.js作为后端环境。让我们开…

Postgresql源码(109)并行框架实例与分析

1 PostgreSQL并行参数 系统参数 系统总worker限制&#xff1a;max_worker_processes 默认8 系统总并发限制&#xff1a;max_parallel_workers 默认8 单Query限制&#xff1a;max_parallel_workers_per_gather 默认2 表参数限制&#xff1a;parallel_workers alter table tbl …

针对高可靠性和高性能优化的1200V硅碳化物沟道MOSFET

目录 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance摘要信息解释研究了什么文章创新点文章的研究方法文章的结论 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance 摘要 本文详…