一、安装部署
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>