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

【微服务】SpringBoot 整合高性能时序数据库 Apache IoTDB 实战操作详解

目录

一、前言

二、Apache IoTDB 介绍

2.1 Apache IoTDB 是什么

2.2 Apache IoTDB 核心特点

2.3 Apache IoTDB 应用场景

2.4 与其他时序数据库对比

三、Docker 部署Apache IoTDB

3.1 创建本地数据映射目录

3.2 启动容器

3.3 使用cli命令连接

3.4 基本使用

四、与SpringBoot 的整合

4.1 前置准备

4.2 基于iotdb-session 的整合过程

4.2.1 添加核心依赖

4.2.2 添加配置文件

4.2.3 添加iotdb配置类

4.2.4 添加数据模型

4.2.5 添加操作iotdb的工具类

4.3 基于iotdb-session的整合测试

4.3.1 提供完整测试代码

4.3.2 核心用例测试

4.4 基于JDBC 方式整合过程

4.4.1 导入核心依赖

4.4.2 添加配置文件

4.4.3 增加数据库连接配置

4.4.4 增加实体值映射对象

4.4.5 增加操作数据库的jpa类型

4.4.6 自定义业务层的service类

4.4.7 自定义接口类

4.4.8 效果测试

五、写在文末


一、前言

在当下数字化深入广泛应用到各个领域,各类物联网设备的使用越来越多,比如各自智能穿戴设备,智能家具,车联网等。这些设备源源不断地产生大量的时序数据,比如传感器读数、设备状态信息等。通常来说,这在这个领域中,时序数据库作为专门存储和管理时序数据的数据库系统,是承载和处理这些数据的关键基础设施。选择合适的时序数据库对于确保数据的高效存储、快速查询和深入分析至关重要。本文以Apache IoTDB为例,从部署到实际在springboot项目中的整合进行详细的分享。

二、Apache IoTDB 介绍

2.1 Apache IoTDB 是什么

Apache IoTDB(Internet of Things Database)是一款开源、高性能的物联网原生时序数据库管理系统,它起源于清华大学,并于2020年9月成为Apache软件基金会的顶级项目。官网:https://iotdb.apache.org/

2.2 Apache IoTDB 核心特点

Apache IoTDB 作为一款性能优异的时序数据库,具备如下核心特点

具体来说,其核心特点的详细说明如下:

  1. 高效的数据管理与查询

    1. 灵活的元数据管理:

      • 采用树状结构组织元数据,一个实例可包含多个存储组(Storage Group,类似于Namespace或Database的概念),一个存储组里包含多个设备(Device),每个设备包含多个测量值(Measurement)。

    2. 丰富的查询语义:

      • 除了基础操作,IoTDB 支持跨设备和传感器的时间对齐查询,以及在时间维度上的聚合(降采样)等。

  2. 优化的存储与高性能

    1. 专为时序数据设计的TsFile:

      • TsFile是一种为物联网设备时序数据存储定制的紧致列式存储文件格式,支持有损、无损等多种高效编码及专有压缩算法。

    2. 高性能读写:

      • IoTDB中可以支持数百万个低功耗和智能联网设备的高速写访问,并提供数据快速读取访问以查询。基准测试表明其读写性能优于一些其他时序数据库。

  3. 灵活的部署与集成

    1. 端-边-云协同架构:

      • IoTDB提供端云一体化的解决方案。在边缘端,提供轻量化的TsFile管理能力;在云端,提供高性能的数据读写以及丰富的查询能力。

    2. 无缝对接大数据生态:

      • IoTDB支持与Hadoop、Spark、Flink等大数据处理框架进行深度集成,并提供了相应的连接器,同时也支持Grafana等可视化工具。

  4. 高可用与可扩展性

    1. 分布式集群支持:IoTDB支持分布式部署,通过Raft协议来确保数据的一致性。系统支持自动负载均衡,并具备高可用性,单节点失效不影响集群服务。

2.3 Apache IoTDB 应用场景

IoTDB 应用场景非常广泛,主要包括如下相关的领域:

  • 工业物联网(IIoT):

    • 在钢铁冶炼、制造业等场景,用于生产全流程实时数据采集与分析,助力产能提升、质量优化与能耗降低

  • 能源电力:

    • 管理发电、输电、用电等全环节数据,提升能效、保障电网安全

  • 航空航天:

    • 处理实时遥测与试飞数据,实现关键系统精准监测,推动设计优化与安全管控

  • 交通运输:

    • 应用于铁路、地铁等场景的智能调度与高效运维,满足高实时、高可靠管理需求

  • 智能家居:

    • 通过监测并控制各种家用设备状态,处理设备产生的时序数据,提供个性化服务

小结:

总的来说,Apache IoTDB通过其物联网原生的数据模型、高性能的存储引擎、灵活的部署方式以及与大数据生态的深度集成,为企业处理物联网时序数据提供了一个高效、可靠且低总体拥有成本的解决方案。特别是在工业物联网等对数据管理要求严苛的场景中,IoTDB 展现出了独特的价值。

2.4 与其他时序数据库对比

作为一款时序数据库,相比市面上其他的时序数据库 ,Apache IoTDB有其独特的优势,下面列举了Apache IoTDB与其他主流的时序数据库的多维度对比

对比维度Apache IoTDBInfluxDBTimescaleDBOpenTSDB
核心架构自研存储引擎 (TsFile),端-边-云协同自研存储引擎 (TSM)基于 PostgreSQL 的扩展依赖 HBase 生态
数据模型树形结构,贴合设备层级基于标签 (Tag) 的扁平模型关系表基于标签 (Tag) 的扁平模型
查询语言类SQL (IoTDB-SQL)InfluxQL, Flux标准 SQL (兼容PostgreSQL)RESTful API
压缩与成本超高压缩比,显著降低存储成本支持压缩支持压缩压缩能力依赖HBase
写入性能稳定千万级数据点/秒写入,优化乱序数据高性能写入高性能写入性能受HBase制约
复杂查询强,支持原生时间对齐、跨设备聚合支持聚合,但时间对齐能力不如IoTDB支持,依赖标准SQL能力功能相对基础
部署与运维轻量级,组件简单,运维成本低单机版简单,集群版闭源依赖PostgreSQL生态依赖HBase等组件,运维复杂

三、Docker 部署Apache IoTDB

为了方便后续的使用以及在项目中整合,接下来演示如何基于Docker环境快速部署Apache IoTDB服务,参考下面的操作过程。注意提前准备好服务器的docker环境。完整使用手册:

https://iotdb.apache.org/zh/UserGuide/V1.3.x/QuickStart/QuickStart_apache.html

3.1 创建本地数据映射目录

创建两个目录,作为容器的数据卷

mkdir data
mkdir logs

3.2 启动容器

使用下面的命令启动容器

命令参数说明:

  • -d:容器在后台运行。

  • --name iotdb:为容器指定一个名称,方便管理。

  • -p 6667:6667:将容器的RPC服务端口(6667)映射到主机,这是客户端连接数据库的端口。

  • -v iotdb_data:/iotdb/data-v iotdb_logs:/iotdb/logs:使用Docker卷(volume)持久化存储数据和日志,避免容器删除后数据丢失。

启动后,你可以使用以下命令进入容器内部,并通过CLI工具连接数据库

3.3 使用cli命令连接

使用下面的命令进入到容器内部

docker exec -it iotdb bash

进入到容器内部后可以看到很多脚本

在容器内使用CLI连接,执行下面的命令:

3.4 基本使用

基于上一步,连接成功后,你会看到IoTDB的命令行界面。可以尝试执行以下SQL命令:

-- 显示现有数据库
SHOW DATABASES;-- 创建测试数据库
CREATE DATABASE root.test;-- 创建时间序列
CREATE TIMESERIES root.test.device1.temperature WITH DATATYPE=FLOAT, ENCODING=PLAIN;
CREATE TIMESERIES root.test.device1.humidity WITH DATATYPE=FLOAT, ENCODING=PLAIN;-- 插入数据
INSERT INTO root.test.device1(timestamp, temperature, humidity) VALUES(100, 25.6, 60.5);-- 查询数据
SELECT * FROM root.test.device1;

操作过程:

1)显示数据库

目前还没有创建过任何数据库

2)创建一个测试数据库

3)创建时间序列并插入数据

4)查询数据

上一步数据插入成功后,查询一下数据

更多的SQL相关的操作可以参考官方提供的文档:https://iotdb.apache.org/zh/UserGuide/V1.3.x/SQL-Manual/SQL-Manual.html

四、与SpringBoot 的整合

IoTDB环境搭好后,下面介绍IoTDB如何与springboot进行整合使用。主流的整合方式主要有两种,基于session的整合使用,以及基于JDBC的使用方式,下面分别做完整的介绍。

4.1 前置准备

为了方便后续整合顺利,需要对基本的技术栈做补充说明:

  • JDK,17;

  • Maven ,3.6.3;

  • Idea , >= 2022.2;

  • springboot, 3.2.0;

基于上述的技术栈,提前搭建一个springboot工程。

4.2 基于iotdb-session 的整合过程

4.2.1 添加核心依赖

其他的依赖可以根据自身的需要酌情添加

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 使用 Session 方式 (推荐) --><dependency><groupId>org.apache.iotdb</groupId><artifactId>iotdb-session</artifactId><version>1.3.1</version> <!-- 建议使用最新稳定版本 --></dependency></dependencies>

4.2.2 添加配置文件

在工程的yml配置文件中增加下面的配置信息

server:port: 8081logging:level:root: infospring:iotdb:ip: 你的IPport: 6667user: rootpassword: rootfetch-size: 10000

4.2.3 添加iotdb配置类

自定义iotdb配置类,该类用于在工程启动的时候初始化iotdb的数据库会话连接

package com.congge.config;import org.apache.iotdb.rpc.IoTDBConnectionException;
import org.apache.iotdb.session.Session;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class IotDBConfig {@Value("${spring.iotdb.ip}")private String ip;@Value("${spring.iotdb.port}")private int port;@Value("${spring.iotdb.user}")private String user;@Value("${spring.iotdb.password}")private String password;@Value("${spring.iotdb.fetch-size}")private int fetchSize;@Beanpublic Session iotSession() {Session session = new Session(ip, port, user, password, fetchSize);try {session.open(); // 打开连接} catch (IoTDBConnectionException e) {e.printStackTrace();}return session;}
}

4.2.4 添加数据模型

自定义一个数据对象,类似于与表的映射实体类对象,定义与表中保存的映射字段信息

package com.congge.config;import java.util.ArrayList;
import java.util.List;/*** 自定义设备数据实体类*/
public class DeviceData {private long timestamp;private String deviceId;private double temperature;private double humidity;private boolean status;// 构造器、getter、setterpublic DeviceData() {}public DeviceData(long timestamp, String deviceId, double temperature, double humidity, boolean status) {this.timestamp = timestamp;this.deviceId = deviceId;this.temperature = temperature;this.humidity = humidity;this.status = status;}public long getTimestamp() { return timestamp; }public void setTimestamp(long timestamp) { this.timestamp = timestamp; }public String getDeviceId() { return deviceId; }public void setDeviceId(String deviceId) { this.deviceId = deviceId; }public double getTemperature() { return temperature; }public void setTemperature(double temperature) { this.temperature = temperature; }public double getHumidity() { return humidity; }public void setHumidity(double humidity) { this.humidity = humidity; }public boolean isStatus() { return status; }public void setStatus(boolean status) { this.status = status; }/*** 转换为测量值列表*/public List<String> getMeasurements() {List<String> measurements = new ArrayList<>();measurements.add("temperature");measurements.add("humidity");measurements.add("status");return measurements;}/*** 转换为值列表*/public List<String> getValues() {List<String> values = new ArrayList<>();values.add(String.valueOf(temperature));values.add(String.valueOf(humidity));values.add(String.valueOf(status));return values;}@Overridepublic String toString() {return String.format("DeviceData{timestamp=%d, deviceId='%s', temperature=%.2f, humidity=%.2f, status=%s}",timestamp, deviceId, temperature, humidity, status);}
}

4.2.5 添加操作iotdb的工具类

实际开发中,该工具类可以作为通用的业务工具类全局使用,根据自己的实际需求进行封装

package com.congge.config;import jakarta.annotation.Resource;
import org.apache.iotdb.isession.SessionDataSet;
import org.apache.iotdb.rpc.IoTDBConnectionException;
import org.apache.iotdb.rpc.StatementExecutionException;
import org.apache.iotdb.session.Session;
import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Service
public class IotDBService {@Resourceprivate Session iotSession;/*** 创建存储组*/public void createStorageGroup(String groupName) throws IoTDBConnectionException, StatementExecutionException {try {iotSession.setStorageGroup(groupName);System.out.println("存储组创建成功: " + groupName);} catch (StatementExecutionException e) {if (e.getMessage().contains("already exists")) {System.out.println("存储组已存在: " + groupName);} else {throw e;}}}/*** 创建时间序列*/public void createTimeSeries(String path, TSDataType dataType, TSEncoding encoding)throws IoTDBConnectionException, StatementExecutionException {try {iotSession.createTimeseries(path, dataType, encoding, CompressionType.SNAPPY);System.out.println("时间序列创建成功: " + path);} catch (StatementExecutionException e) {if (e.getMessage().contains("already exists")) {System.out.println("时间序列已存在: " + path);} else {throw e;}}}/*** 插入单条记录*/public void insertRecord(DeviceData data) throws IoTDBConnectionException, StatementExecutionException {String deviceId = "root.test2." + data.getDeviceId();iotSession.insertRecord(deviceId, data.getTimestamp(),data.getMeasurements(), data.getValues());System.out.println("数据插入成功: " + data);}/*** 批量插入记录 - 性能更优*/public void insertRecords(List<DeviceData> dataList) throws IoTDBConnectionException, StatementExecutionException {List<String> deviceIds = new ArrayList<>();List<Long> timestamps = new ArrayList<>();List<List<String>> measurementsList = new ArrayList<>();List<List<String>> valuesList = new ArrayList<>();for (DeviceData data : dataList) {deviceIds.add("root.test2." + data.getDeviceId());timestamps.add(data.getTimestamp());measurementsList.add(data.getMeasurements());valuesList.add(data.getValues());}iotSession.insertRecords(deviceIds, timestamps, measurementsList, valuesList);System.out.println("批量插入成功,共 " + dataList.size() + " 条记录");}/*** 执行查询*/public List<DeviceData> executeQuery(String sql) throws IoTDBConnectionException, StatementExecutionException {List<DeviceData> result = new ArrayList<>();SessionDataSet dataSet = iotSession.executeQueryStatement(sql);try {SessionDataSet.DataIterator iterator = dataSet.iterator();while (iterator.next()) {DeviceData data = new DeviceData();data.setTimestamp(Long.parseLong(iterator.getString("Time")));// 根据实际查询的字段调整if (iterator.findColumn("root.test2.device2.temperature") != -1) {data.setTemperature(Double.parseDouble(iterator.getString("root.test2.device2.temperature")));}if (iterator.findColumn("root.test2.device2.humidity") != -1) {data.setHumidity(Double.parseDouble(iterator.getString("root.test2.device2.humidity")));}if (iterator.findColumn("root.test2.device2.status") != -1) {data.setStatus(Boolean.parseBoolean(iterator.getString("root.test2.device2.status")));}result.add(data);}} finally {dataSet.close();}return result;}/*** 清理测试数据*/public void cleanupTestData() throws IoTDBConnectionException, StatementExecutionException {try {iotSession.deleteStorageGroup("root.test");System.out.println("测试数据清理完成");} catch (StatementExecutionException e) {System.out.println("无需清理测试数据: " + e.getMessage());}}/*** 检查连接状态*/public boolean checkConnection() {try {iotSession.executeQueryStatement("SHOW DATABASES").close();return true;} catch (Exception e) {return false;}}
}

4.3 基于iotdb-session的整合测试

接下来,通过测试代码分别测试下工具类中的核心方法。

4.3.1 提供完整测试代码

参考下面的测试代码

import com.congge.BootApp;
import com.congge.config.DeviceData;
import com.congge.config.IotDBService;
import jakarta.annotation.Resource;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import java.util.ArrayList;
import java.util.List;@SpringBootTest(classes = BootApp.class)
public class IoDbTest {private static final String TEST_STORAGE_GROUP = "root.test2";private static final String TEST_DEVICE = "device2";@Resourceprivate IotDBService iotDBService;@Test@DisplayName("测试数据库连接")void testConnection() {iotDBService.checkConnection();System.out.println("✅ 数据库连接测试通过");}@Test@DisplayName("测试创建存储组/数据库")void testCreateStorageGroup() {try {iotDBService.createStorageGroup(TEST_STORAGE_GROUP);System.out.println("✅ 存储组创建测试通过");} catch (Exception e) {System.out.println("❌ 创建存储组失败 : " + e.getMessage());}}@Test@DisplayName("测试创建时间序列")void testCreateTimeSeries() {try {iotDBService.createTimeSeries("root.test2.device2.temperature", TSDataType.FLOAT, TSEncoding.PLAIN);iotDBService.createTimeSeries("root.test2.device2.humidity", TSDataType.FLOAT, TSEncoding.PLAIN);iotDBService.createTimeSeries("root.test2.device2.status", TSDataType.BOOLEAN, TSEncoding.PLAIN);System.out.println("✅ 时间序列创建测试通过");} catch (Exception e) {System.out.println("❌ 创建时间序列失败 : " + e.getMessage());}}@Test@DisplayName("测试插入单条记录")void testInsertSingleRecord() {try {DeviceData data = new DeviceData(System.currentTimeMillis(),TEST_DEVICE,25.5,60.2,true);iotDBService.insertRecord(data);System.out.println("✅ 单条记录插入测试通过");} catch (Exception e) {System.out.println("❌ 插入单条记录失败 : " + e.getMessage());}}@Test@DisplayName("测试批量插入记录")void testBatchInsert() {try {List<DeviceData> dataList = new ArrayList<>();long baseTime = System.currentTimeMillis();for (int i = 0; i < 10; i++) {DeviceData data = new DeviceData(baseTime + (i * 1000), // 每秒一条数据TEST_DEVICE,20.0 + i * 0.5,50.0 + i * 2,i % 2 == 0);dataList.add(data);}iotDBService.insertRecords(dataList);System.out.println("✅ 批量插入测试通过,插入了 " + dataList.size() + " 条记录");} catch (Exception e) {System.out.println("❌ 批量插入失败 : " + e.getMessage());}}@Test@DisplayName("测试数据查询")void testDataQuery() {try {// 先插入一些测试数据DeviceData testData = new DeviceData(System.currentTimeMillis() - 5000,TEST_DEVICE,23.5,55.5,true);iotDBService.insertRecord(testData);// 查询数据String sql = "SELECT * FROM root.test.device1 WHERE time > " + (System.currentTimeMillis() - 10000);List<DeviceData> results = iotDBService.executeQuery(sql);System.out.println("✅ 数据查询测试通过,查询到 " + results.size() + " 条记录");results.forEach(data -> System.out.println("   - " + data));} catch (Exception e) {System.out.println("❌ 数据查询失败 : " + e.getMessage());}}@Test@DisplayName("测试条件查询")void testConditionalQuery() {try {String sql = "SELECT temperature, humidity FROM root.test.device1 WHERE temperature > 20.0";List<DeviceData> results = iotDBService.executeQuery(sql);System.out.println("✅ 条件查询测试通过,查询到 " + results.size() + " 条温度>20.0的记录");} catch (Exception e) {System.out.println("❌ 条件查询失败 : " + e.getMessage());}}}

4.3.2 核心用例测试

1)测试数据库连接

运行测试代码,效果如下:

2)创建存储组测试

运行测试代码,效果如下:

通过cli命令可以看到创建成功

3)创建时间序列测试

运行测试代码,效果如下:

4)批量插入数据测试

运行测试代码,效果如下:

使用cli 命令查询下数据,可以看到数据插入成功了

5)查询数据测试

运行测试代码,效果如下:

6)指定条件查询测试

运行测试代码,效果如下:

4.4 基于JDBC 方式整合过程

下面再提供基于JDBC 的方式的整合过程,也是开发中比较常用的一种,参考下面的操作过程。

4.4.1 导入核心依赖

pom文件中导入下面的核心依赖

<!-- jdbc 的方式 -->
<dependency><groupId>org.apache.iotdb</groupId><artifactId>iotdb-jdbc</artifactId><version>1.2.1</version>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

4.4.2 添加配置文件

在工程的yml配置文件中添加下面的配置信息

server:port: 8081logging:level:root: infospring:datasource:# JDBC URL格式:jdbc:iotdb://{ip}:{port}/url: jdbc:iotdb://你的IP:6667/username: rootpassword: root# 指定IoTDB的JDBC驱动类driver-class-name: org.apache.iotdb.jdbc.IoTDBDriver# HikariCP连接池配置(可选)hikari:maximum-pool-size: 10minimum-idle: 2connection-timeout: 30000idle-timeout: 600000max-lifetime: 1800000# 自定义IoTDB配置项
iotdb:# 查询默认获取批次大小fetch-size: 10000

4.4.3 增加数据库连接配置

该配置类用于初始化与数据库的连接

package com.congge.config.jdbc;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;@Configuration
public class IotDBConfig {@Beanpublic JdbcTemplate iotdbJdbcTemplate(javax.sql.DataSource dataSource) {return new JdbcTemplate(dataSource);}
}

4.4.4 增加实体值映射对象

对象与数据库的表字段进行对应

  • 特别注意:实体类的第一个字段必须是Time(long类型),后续字段顺序需与查询结果严格对应。

package com.congge.config.jdbc;import lombok.Data;@Data
public class DeviceData {// 必须第一个字段是Time,存储时间戳:cite[10]private Long time;// 对应时间序列的字段,顺序需与SQL查询字段顺序一致private String deviceId;private Double temperature;private Double humidity;private Boolean status;// 全参构造器public DeviceData(Long time, String deviceId, Double temperature, Double humidity, Boolean status) {this.time = time;this.deviceId = deviceId;this.temperature = temperature;this.humidity = humidity;this.status = status;}// 无参构造器public DeviceData() {}
}

4.4.5 增加操作数据库的jpa类型

创建Repository类来封装IoTDB的CRUD操作

package com.congge.config.jdbc;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;@Repository
public class IotDBRepository {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** 创建存储组*/public void createStorageGroup(String storageGroup) {String sql = "SET STORAGE GROUP TO ?";jdbcTemplate.update(sql, storageGroup);}/*** 创建时间序列*/public void createTimeSeries(String path, String dataType, String encoding) {String sql = "CREATE TIMESERIES ? WITH DATATYPE=?, ENCODING=?";jdbcTemplate.update(sql, path, dataType, encoding);}/*** 插入单条设备数据*/public void insertDeviceData(DeviceData data) {// 建议使用参数化查询,避免SQL注入String sql = "INSERT INTO root.test3.device3(timestamp, temperature, humidity, status) VALUES(?, ?, ?, ?)";jdbcTemplate.update(sql,data.getTime(),data.getTemperature(),data.getHumidity(),data.getStatus());}/*** 批量插入设备数据 - 高性能写入*/public void batchInsertDeviceData(List<DeviceData> dataList) {String sql = "INSERT INTO root.test3.device3(timestamp, temperature, humidity, status) VALUES(?, ?, ?, ?)";jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {@Overridepublic void setValues(PreparedStatement ps, int i) throws SQLException {DeviceData data = dataList.get(i);ps.setLong(1, data.getTime());ps.setDouble(2, data.getTemperature());ps.setDouble(3, data.getHumidity());ps.setBoolean(4, data.getStatus());}@Overridepublic int getBatchSize() {return dataList.size();}});}/*** 查询设备数据 - 注意字段顺序必须与实体类定义一致:cite[10]*/public List<DeviceData> queryDeviceData(Long startTime, Long endTime) {// 明确指定查询字段及其顺序,避免使用 SELECT *:cite[10]String sql = "SELECT Time, device3.temperature, device3.humidity, device3.status " +"FROM root.test3.device3 " +"WHERE time >= ? AND time <= ? " +"ORDER BY time DESC";return jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(DeviceData.class),startTime, endTime);}/*** 条件查询 - 按温度阈值筛选*/public List<DeviceData> queryByTemperatureThreshold(Double tempThreshold) {String sql = "SELECT Time, device3.temperature, device3.humidity, device3.status " +"FROM root.test3.device3 " +"WHERE device3.temperature > ? " +"ORDER BY time DESC";return jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(DeviceData.class),tempThreshold);}/*** 删除数据*/public void deleteData(Long endTime) {String sql = "DELETE FROM root.test3.device3 WHERE time <= ?";jdbcTemplate.update(sql, endTime);}
}

4.4.6 自定义业务层的service类

自定义一个Service类来处理与实际业务相关的逻辑

package com.congge.config.jdbc;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Service
public class DeviceDataService {@Autowiredprivate IotDBRepository iotDBRepository;/*** 初始化IoTDB模式(存储组和时间序列)*/public void initializeSchema() {try {// 创建存储组iotDBRepository.createStorageGroup("root.test3");// 创建时间序列 - 实际使用中可能需要根据IoTDB版本调整语法// iotDBRepository.createTimeSeries("root.test.device1.temperature", "FLOAT", "PLAIN");// iotDBRepository.createTimeSeries("root.test.device1.humidity", "FLOAT", "PLAIN");// iotDBRepository.createTimeSeries("root.test.device1.status", "BOOLEAN", "PLAIN");} catch (Exception e) {System.err.println("初始化模式失败(可能已存在): " + e.getMessage());}}/*** 生成并插入测试数据*/public void generateAndInsertTestData() {List<DeviceData> testData = new ArrayList<>();long baseTime = System.currentTimeMillis();// 生成10条测试数据for (int i = 0; i < 10; i++) {DeviceData data = new DeviceData(baseTime + (i * 1000), // 时间戳递增"device3",20.0 + i * 0.5,       // 温度递增50.0 + i * 2,         // 湿度递增i % 2 == 0            // 状态交替);testData.add(data);}// 批量插入iotDBRepository.batchInsertDeviceData(testData);System.out.println("成功插入 " + testData.size() + " 条测试数据");}/*** 获取最近N小时的数据*/public List<DeviceData> getRecentData(int hours) {long endTime = System.currentTimeMillis();long startTime = endTime - (hours * 60 * 60 * 1000L);return iotDBRepository.queryDeviceData(startTime, endTime);}/*** 根据温度阈值查询数据*/public List<DeviceData> getDataByTemperature(Double threshold) {return iotDBRepository.queryByTemperatureThreshold(threshold);}
}

4.4.7 自定义接口类

为方便效果测试,添加一个接口类,参考下面的代码

package com.congge.config.jdbc;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/api/device")
public class DeviceDataController {@Autowiredprivate DeviceDataService deviceDataService;/*** 初始化数据库模式* localhost:8081/api/device/init*/@GetMapping("/init")public String initializeDatabase() {try {deviceDataService.initializeSchema();return "数据库初始化成功";} catch (Exception e) {return "数据库初始化失败: " + e.getMessage();}}/*** 插入测试数据* localhost:8081/api/device/test-data*/@GetMapping("/test-data")public String insertTestData() {try {deviceDataService.generateAndInsertTestData();return "测试数据插入成功";} catch (Exception e) {return "测试数据插入失败: " + e.getMessage();}}/*** 查询最近数据* localhost:8081/api/device/recent*/@GetMapping("/recent")public List<DeviceData> getRecentData(@RequestParam(defaultValue = "1") int hours) {return deviceDataService.getRecentData(hours);}/*** 按温度查询* localhost:8081/api/device/by-temperature*/@GetMapping("/by-temperature")public List<DeviceData> getByTemperature(@RequestParam Double threshold) {return deviceDataService.getDataByTemperature(threshold);}
}

4.4.8 效果测试

启动工程后,对上面的接口进行测试。

1)测试数据初始化

调用一下接口

接口执行成功后,通过cli 命令可以看到数据库初始化成功了

2)测试插入数据

执行接口

接口执行成功后,通过cli 命令可以看到数据插入成功了

五、写在文末

本文通过较大的篇幅详细介绍了新一代时序数据库Apache IoTDB的使用,从服务的部署到与springboot的整合完整过程,最后通过实际案例代码演示了具体的使用,更多的细节有兴趣的同学还可以基于此继续完善,希望对看到的同学有用,本篇到此结束,感谢观看。

http://www.dtcms.com/a/617900.html

相关文章:

  • 【电路笔记】-单稳态多谐振荡器
  • Java数据结构-Map和Set-通配符?-反射-枚举-Lambda
  • 在那里能找到网站网络营销与网站推广的区别
  • 架构之路(六):把框架拉出来
  • 【Linux驱动开发】Linux SPI 通信详解:从硬件到驱动再到应用
  • 【ASP.NET进阶】Controller层核心:Action方法全解析,从基础到避坑
  • Imec实现了GaN击穿电压的记录
  • Streaming ELT with Flink CDC · Iceberg Sink
  • AI(新手)
  • 海南城乡建设厅网站百度竞价关键词查询
  • QT开发——常用控件(2)
  • 【Java架构师体系课 | MySQL篇】⑥ 索引优化实战二
  • Spring Boot、Redis、RabbitMQ 在项目中的核心作用详解
  • 做完整的网站设计需要的技术长治建立公司网站的步骤
  • 南宁京象建站公司网站建设留言板实验心得
  • AI、LLM全景图
  • pip升级已安装包方法详解
  • 【Linux日新月异(六)】CentOS 7网络命令深度解析:从传统到现代网络管理
  • LangChain 构建 AI 代理(Agent)
  • 人工智能训练师备考——3.1.1题解
  • 【RL】ORPO: Monolithic Preference Optimization without Reference Model
  • 公益平台网站怎么做网站跳出
  • QT的5种标准对话框
  • 用Rust构建一个OCR命令行工具
  • 网站代码大全国内网站设计作品欣赏
  • LeetCode 热题 100——子串——滑动窗口最大值
  • CPP(容器)STL:
  • 【Java常用API】----- Math
  • RAG 系统 “检索 - 筛选 - 生成” 完整流程
  • 时间复杂度 和 嵌入式时钟概念 有关系。 我的理由是:时钟经常需要计算频率,而频率往往需要和时间进行计数次数i 。 时间复杂度就像是计数次数i