IotDb的安装、连接和应用开发

一、 安装部署

  • 拉取镜像
docker pull  apache/iotdb:1.3.5-standalone
  • 启动脚本:docker-compose.yml
version: '3.8'

services:
  iotdb:
    image: apache/iotdb:1.3.5-standalone
    container_name: iotdb-springboot
    ports:
      - "6667:6667"  # RPC端口
      - "8080:8080"  # Web UI端口
      - "31999:31999" # InfluxDB端口
    environment:
      - JAVA_OPTS=-DIOTDB_HOME=/iotdb -Xmx2G -Xms1G
      - ENABLE_KERBEROS=false
      - IOTDB_CONFIGNODE_CONSENSUS_PROTOCOL_CLASS=standalone
      - IOTDB_DATANODE_CONSENSUS_PROTOCOL_CLASS=standalone
      - IOTDB_SCHEMA_ENGINE_MODE=Memory
      - enable_raft_log_persistence=false
    volumes:
      - iotdb_data:/iotdb/data
      - iotdb_logs:/iotdb/logs
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/localhost/6667 && echo -e '\x00\x00\x00\x01\x00\x00\x00\x00' >&3 && head -c 8 <&3"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    networks:
      - default

二、IDEA 连接 IoTDB

2.1 下载驱动文件

  • 下载连接
    在这里插入图片描述

  • 添加 IDEA驱动
    在这里插入图片描述

  • IDEA连接数据库
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • User: root

  • Passwords: your_passwords

  • URL: jdbc:iotdb://your_ip:6667/

  • 测试连接按钮,SQL语句执行返回数据有异常,但是没关系,也证明连接成功了

select status from root.test.test

在这里插入图片描述

三、基本使用

3.1 基础配置

- 添加pom依赖

        <!-- Apache IoTDB 依赖 -->
        <dependency>
            <groupId>org.apache.iotdb</groupId>
            <artifactId>iotdb-session</artifactId>
            <version>2.0.5</version>
        </dependency>

- yaml配置

# IoTDB 配置
iotdb:
  host: ${IOTDB_HOST:localhost}
  port: ${IOTDB_PORT:6667}
  username: ${IOTDB_USERNAME:root}
  password: ${IOTDB_PASSWORD:root}
  node-urls: "" # 多节点集群URL,用逗号分隔,例如: "host1:6667,host2:6667,host3:6667"

- 创建Java config:IoTDBConfig

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 描述: IoTDB 配置类
 *
 * @author water
 * @version 2.0.5
 * @date 2026-01-05
 */
@Component
@ConfigurationProperties(prefix = "iotdb")
public class IoTDBConfig {
    private String host = "localhost";
    private int port = 6667;
    private String username = "root";
    private String password = "root";
    private String nodeUrls; // 支持多个节点的URL

    // getter 和 setter 方法
    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    
    public String getNodeUrls() {
        return nodeUrls;
    }

    public void setNodeUrls(String nodeUrls) {
        this.nodeUrls = nodeUrls;
    }
}

- IOTDB 初始化 :IoTDBInitializer

import com.springboot4.java25.service.IoTDBService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

/**
 * 描述: IoTDB 初始化组件,在应用启动时执行必要的初始化操作
 *
 * @author water
 * @version 1.0.0
 * @date 2026-01-05
 */
@Component
public class IoTDBInitializer implements CommandLineRunner {
    private static final Logger logger = LoggerFactory.getLogger(IoTDBInitializer.class);

    @Autowired
    private IoTDBService ioTDBService;

    @Override
    public void run(String... args) throws Exception {
        logger.info("开始初始化 IoTDB...");
        
        try {
            // 创建默认存储组
            ioTDBService.createStorageGroup("root.default");
            logger.info("IoTDB 初始化完成");
        } catch (Exception e) {
            logger.error("IoTDB 初始化失败", e);
        }
    }
}

3.2 应用开发

3.2.1 启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.logging.Logger;

/**
 * 描述: 25新特性 介绍Java 25版本的新特性和改进
 *
 * @author water
 * @version 1.0.0
 * @date 2025-12-29 11:52
 */
@SpringBootApplication
public class Java25Springboot4Application {
    public static final Logger logger = Logger.getLogger(Java25Springboot4Application.class.getName());

    public static void main(String[] args) {
        SpringApplication.run(Java25Springboot4Application.class, args);
    }
}

3.2.2 实体对象 model

package com.springboot4.java25.model;

/**
 * 描述: IoTDB 数据模型
 *
 * @author water
 * @version 1.0.0
 * @date 2026-01-05
 */
public class IoTDataPoint {
    private String deviceId;
    private String measurement;
    private Object value;
    private long timestamp;

    public IoTDataPoint() {
    }

    public IoTDataPoint(String deviceId, String measurement, Object value, long timestamp) {
        this.deviceId = deviceId;
        this.measurement = measurement;
        this.value = value;
        this.timestamp = timestamp;
    }

    // getter 和 setter 方法
    public String getDeviceId() {
        return deviceId;
    }

    public void setDeviceId(String deviceId) {
        this.deviceId = deviceId;
    }

    public String getMeasurement() {
        return measurement;
    }

    public void setMeasurement(String measurement) {
        this.measurement = measurement;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }
}

3.2.3 控制器 controller

import com.springboot4.java25.service.IoTDBService;
import org.apache.iotdb.isession.SessionDataSet;
import org.apache.tsfile.enums.TSDataType;
import org.apache.tsfile.read.common.Field;
import org.apache.tsfile.read.common.RowRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 描述: IoTDB 控制器,提供 REST API 接口来操作 IoTDB
 *
 * @author water
 * @version 1.0.0
 * @date 2026-01-05
 */
@RestController
@RequestMapping("/api/iotdb")
public class IoTDBController {

    @Autowired
    private IoTDBService ioTDBService;

    /**
     * 创建数据库
     */
    @PostMapping("/database/{database}")
    public ResponseEntity<String> createDatabase(@PathVariable String database) {
        try {
            ioTDBService.createDatabase(database);
            return ResponseEntity.ok("数据库 " + database + " 创建成功");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("创建数据库失败: " + e.getMessage());
        }
    }

    /**
     * 创建数据库 - 通过请求参数
     */
    @PostMapping(value = "/database", consumes = {"application/x-www-form-urlencoded", "multipart/form-data"})
    public ResponseEntity<String> createDatabaseParam(@RequestParam String database) {
        try {
            ioTDBService.createDatabase(database);
            return ResponseEntity.ok("数据库 " + database + " 创建成功");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("创建数据库失败: " + e.getMessage());
        }
    }

    /**
     * 创建存储组
     */
    @PostMapping("/storagegroup/{storageGroup}")
    public ResponseEntity<String> createStorageGroup(@PathVariable String storageGroup) {
        try {
            ioTDBService.createStorageGroup(storageGroup);
            return ResponseEntity.ok("存储组 " + storageGroup + " 创建成功");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("创建存储组失败: " + e.getMessage());
        }
    }

    /**
     * 创建存储组 - 通过请求参数
     */
    @PostMapping(value = "/storagegroup", consumes = {"application/x-www-form-urlencoded", "multipart/form-data"})
    public ResponseEntity<String> createStorageGroupParam(@RequestParam String storageGroup) {
        try {
            ioTDBService.createStorageGroup(storageGroup);
            return ResponseEntity.ok("存储组 " + storageGroup + " 创建成功");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("创建存储组失败: " + e.getMessage());
        }
    }

    /**
     * 插入记录
     */
    @PostMapping(value = "/insert", consumes = {"application/json"})
    public ResponseEntity<String> insertRecord(@RequestBody IoTDBInsertRequest request) {
        try {
            ioTDBService.insertRecord(
                    request.getDeviceId(),
                    request.getTimestamp(),
                    request.getMeasurements(),
                    request.getValues()
            );
            return ResponseEntity.ok("数据插入成功");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("数据插入失败: " + e.getMessage());
        }
    }

    /**
     * 插入记录 - 支持表单数据
     */
    @PostMapping(value = "/insert", consumes = {"application/x-www-form-urlencoded", "multipart/form-data"})
    public ResponseEntity<String> insertRecordForm(
            String deviceId,
            long timestamp,
            @RequestParam List<String> measurements,
            @RequestParam List<String> values) {
        try {
            ioTDBService.insertRecord(deviceId, timestamp, measurements, values);
            return ResponseEntity.ok("数据插入成功");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("数据插入失败: " + e.getMessage());
        }
    }

    /**
     * 批量插入记录
     */
    @PostMapping("/insert/batch")
    public ResponseEntity<String> insertRecords(@RequestBody IoTDBBatchInsertRequest request) {
        try {
            ioTDBService.insertRecords(
                    request.getDeviceIds(),
                    request.getTimestamps(),
                    request.getMeasurementsList(),
                    request.getValuesList()
            );
            return ResponseEntity.ok("批量数据插入成功");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("批量数据插入失败: " + e.getMessage());
        }
    }

    /**
     * SELECT * FROM root.ipark.`001`
     *
     * @param sql 查询语句
     * @return 查询结果
     */
    @GetMapping("/query")
    public ResponseEntity<Object> queryData(@RequestParam String sql) {
        try {
            // 简单验证SQL,防止SQL注入
            if (!isValidQuerySql(sql)) {
                return ResponseEntity.badRequest().body("无效的查询语句");
            }

            SessionDataSet dataSet = ioTDBService.queryData(sql);
            // 将结果转换为JSON格式返回
            List<Object> result = convertDataSetToJson(dataSet);
            dataSet.closeOperationHandle();
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("查询失败: " + e.getMessage());
        }
    }

    /**
     * 执行更安全的查询 - 通过POST请求
     */
    @PostMapping(value = "/query", consumes = {"application/json"})
    public ResponseEntity<Object> queryDataPost(@RequestBody QueryRequest request) {
        try {
            String sql = request.getSql();

            // 简单验证SQL,防止SQL注入
            if (!isValidQuerySql(sql)) {
                return ResponseEntity.badRequest().body("无效的查询语句");
            }

            SessionDataSet dataSet = ioTDBService.queryData(sql);
            List<Object> result = convertDataSetToJson(dataSet);
            dataSet.closeOperationHandle();
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("查询失败: " + e.getMessage());
        }
    }

    /**
     * 简单验证SQL,防止SQL注入
     *
     * @param sql SQL语句
     * @return 是否合法
     */
    private boolean isValidQuerySql(String sql) {
        if (sql == null || sql.trim().isEmpty()) {
            return false;
        }

        String upperSql = sql.trim().toUpperCase();

        // 只允许SELECT语句
        if (!upperSql.startsWith("SELECT")) {
            return false;
        }

        // 检查是否包含潜在的危险关键字
        String[] dangerousKeywords = {"DROP", "DELETE", "UPDATE", "INSERT", "CREATE", "ALTER", "TRUNCATE", "EXEC",
                "CALL"};
        for (String keyword : dangerousKeywords) {
            if (upperSql.contains(keyword)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 将SessionDataSet转换为JSON格式
     *
     * @param dataSet SessionDataSet对象
     * @return JSON格式数据
     * @throws Exception 获取数据时发生的异常
     */
    private List<Object> convertDataSetToJson(SessionDataSet dataSet) throws Exception {
        List<Object> result = new ArrayList<>();

        // 获取列信息
        List<String> columnNames = dataSet.getColumnNames();

        while (dataSet.hasNext()) {
            RowRecord record = dataSet.next();
            Map<String, Object> row = new HashMap<>();

            // 获取记录中的字段列表
            List<Field> fields = record.getFields();

            // 处理时间戳
            long timestamp = record.getTimestamp();
            // 使用UTC时区格式化时间戳
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String formattedTime = sdf.format(new java.util.Date(timestamp));
            row.put(columnNames.getFirst(), formattedTime);

            // 确保字段数量与列名数量一致
            int fieldCount = fields != null ? fields.size() : 0;
            int columnCount = columnNames.size();

            // 安全地处理字段,避免索引越界
            for (int i = 0; i < Math.min(fieldCount, columnCount); i++) {
                String columnName = columnNames.get(i + 1);
                String value = "";

                // 检查字段是否存在
                if (fields != null && i < fields.size() && fields.get(i) != null) {
                    value = fields.get(i).toString();
                } else {
                    value = "null"; // 或者使用适当的默认值
                }

                row.put(columnName, value);
            }

            result.add(row);
        }

        return result;
    }

    /**
     * 查询请求对象
     */
    public static class QueryRequest {
        private String sql;

        public String getSql() {
            return sql;
        }

        public void setSql(String sql) {
            this.sql = sql;
        }
    }

    /**
     * 创建时间序列
     */
    @PostMapping("/timeseries")
    public ResponseEntity<String> createTimeseries(@RequestBody IoTDBTimeseriesRequest request) {
        try {
            ioTDBService.createTimeseries(
                    request.getPath(),
                    request.getDataType(),
                    request.getEncoding(),
                    request.getCompressor()
            );
            return ResponseEntity.ok("时间序列创建成功");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("时间序列创建失败: " + e.getMessage());
        }
    }

    // 请求对象类
    public static class IoTDBInsertRequest {
        private String deviceId;
        private long timestamp;
        private List<String> measurements;
        private List<String> values;

        // getter 和 setter 方法
        public String getDeviceId() {
            return deviceId;
        }

        public void setDeviceId(String deviceId) {
            this.deviceId = deviceId;
        }

        public long getTimestamp() {
            return timestamp;
        }

        public void setTimestamp(long timestamp) {
            this.timestamp = timestamp;
        }

        public List<String> getMeasurements() {
            return measurements;
        }

        public void setMeasurements(List<String> measurements) {
            this.measurements = measurements;
        }

        public List<String> getValues() {
            return values;
        }

        public void setValues(List<String> values) {
            this.values = values;
        }
    }

    public static class IoTDBBatchInsertRequest {
        private List<String> deviceIds;
        private List<Long> timestamps;
        private List<List<String>> measurementsList;
        private List<List<String>> valuesList;

        // getter 和 setter 方法
        public List<String> getDeviceIds() {
            return deviceIds;
        }

        public void setDeviceIds(List<String> deviceIds) {
            this.deviceIds = deviceIds;
        }

        public List<Long> getTimestamps() {
            return timestamps;
        }

        public void setTimestamps(List<Long> timestamps) {
            this.timestamps = timestamps;
        }

        public List<List<String>> getMeasurementsList() {
            return measurementsList;
        }

        public void setMeasurementsList(List<List<String>> measurementsList) {
            this.measurementsList = measurementsList;
        }

        public List<List<String>> getValuesList() {
            return valuesList;
        }

        public void setValuesList(List<List<String>> valuesList) {
            this.valuesList = valuesList;
        }
    }

    public static class IoTDBTimeseriesRequest {
        private String path;
        private TSDataType dataType;
        private String encoding;
        private String compressor;

        // getter 和 setter 方法
        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public TSDataType getDataType() {
            return dataType;
        }

        public void setDataType(TSDataType dataType) {
            this.dataType = dataType;
        }

        public String getEncoding() {
            return encoding;
        }

        public void setEncoding(String encoding) {
            this.encoding = encoding;
        }

        public String getCompressor() {
            return compressor;
        }

        public void setCompressor(String compressor) {
            this.compressor = compressor;
        }
    }
}

3.2.3 控制器 service

import com.springboot4.java25.config.IoTDBConfig;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
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.tsfile.enums.TSDataType;
import org.apache.tsfile.file.metadata.enums.CompressionType;
import org.apache.tsfile.file.metadata.enums.TSEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 描述: IoTDB 服务类,用于处理与 IoTDB 的交互
 *
 * @author water
 * @version 1.0.0
 * @date 2026-01-05
 */
@Service
public class IoTDBService {
    private static final Logger logger = LoggerFactory.getLogger(IoTDBService.class);

    private Session session;

    @Autowired
    private IoTDBConfig config;

    @PostConstruct
    public void init() {
        try {
            Session.Builder builder =
                    new Session.Builder().host(config.getHost()).port(config.getPort()).username(config.getUsername()).password(config.getPassword());

            // 如果配置了多个节点URL
            if (config.getNodeUrls() != null && !config.getNodeUrls().isEmpty()) {
                String[] nodeUrls = config.getNodeUrls().split(",");
                builder.nodeUrls(Arrays.asList(nodeUrls));
            }

            session = builder.build();
            session.open(false);
            logger.info("成功连接到 IoTDB 服务器: {}:{}", config.getHost(), config.getPort());

            // 测试连接
            testConnection();
        } catch (IoTDBConnectionException e) {
            logger.error("连接 IoTDB 服务器失败: {}:{}", config.getHost(), config.getPort(), e);
        }
    }

    /**
     * 测试连接
     */
    private void testConnection() {
        try {
            SessionDataSet dataSet = session.executeQueryStatement("SHOW VERSION");
            logger.info("IoTDB 连接测试成功");
            dataSet.closeOperationHandle();
        } catch (Exception e) {
            logger.error("IoTDB 连接测试失败", e);
        }
    }

    @PreDestroy
    public void close() {
        if (session != null) {
            try {
                session.close();
                logger.info("IoTDB 连接已关闭");
            } catch (IoTDBConnectionException e) {
                logger.error("关闭 IoTDB 连接时出错", e);
            }
        }
    }

    /**
     * 创建数据库(存储组)
     *
     * @param database 数据库名称
     * @throws IoTDBConnectionException    io 异常
     * @throws StatementExecutionException SQL异常
     */
    public void createDatabase(String database) throws IoTDBConnectionException, StatementExecutionException {
        checkStorageGroupOrDataBase(database);
    }


    /**
     * 插入记录
     */
    public void insertRecord(String deviceId, long timestamp, List<String> measurements, List<String> values) throws IoTDBConnectionException, StatementExecutionException {
        // 确保设备ID符合IoTDB路径规范 (root.storageGroup.device)
        deviceId = normalizeDeviceId(deviceId);

        session.insertRecord(deviceId, timestamp, measurements, values);
        logger.info("插入记录: 设备={}, 时间戳={}", deviceId, timestamp);
    }

    /**
     * 批量插入记录
     */
    public void insertRecords(List<String> deviceIds, List<Long> timestamps, List<List<String>> measurementsList,
                              List<List<String>> valuesList) throws IoTDBConnectionException,
            StatementExecutionException {
        // 确保所有设备ID符合IoTDB路径规范 (root.storageGroup.device)
        List<String> normalizedDeviceIds = new ArrayList<>();
        for (String deviceId : deviceIds) {
            normalizedDeviceIds.add(normalizeDeviceId(deviceId));
        }

        session.insertRecords(normalizedDeviceIds, timestamps, measurementsList, valuesList);
        logger.info("批量插入记录: {} 条", deviceIds.size());
    }

    /**
     * 查询数据
     */
    public SessionDataSet queryData(String sql) throws IoTDBConnectionException, StatementExecutionException {
        logger.info("执行查询: {}", sql);

        return session.executeQueryStatement(sql);
    }

    /**
     * 创建时间序列
     */
    public void createTimeseries(String path, TSDataType dataType, String encoding, String compressor) throws IoTDBConnectionException, StatementExecutionException {
        session.createTimeseries(path, dataType, TSEncoding.valueOf(encoding), CompressionType.valueOf(compressor));
        logger.info("创建时间序列: {}", path);
    }

    /**
     * 使用新API创建时间序列
     */
    public void createTimeseriesNewAPI(String path, TSDataType dataType, TSEncoding encoding,
                                       CompressionType compressionType) throws IoTDBConnectionException,
            StatementExecutionException {
        session.createTimeseries(path, dataType, encoding, compressionType);
        logger.info("创建时间序列 (新API): {}", path);
    }

    /**
     * 删除时间序列
     */
    public void deleteTimeseries(String path) throws IoTDBConnectionException, StatementExecutionException {
        session.deleteTimeseries(path);
        logger.info("删除时间序列: {}", path);
    }

    /**
     * 删除存储组
     */
    public void deleteStorageGroup(String storageGroup) throws IoTDBConnectionException, StatementExecutionException {
        session.deleteStorageGroup(storageGroup);
        logger.info("删除存储组: {}", storageGroup);
    }


    /**
     * 创建存储组(数据库)
     *
     * @param storageGroup 存储组名称(storageGroup /数据库)
     * @throws IoTDBConnectionException    io 异常
     * @throws StatementExecutionException SQL异常
     */
    public void createStorageGroup(String storageGroup) throws IoTDBConnectionException, StatementExecutionException {
        checkStorageGroupOrDataBase(storageGroup);
    }


    /**
     * 检查存储组(数据库),并创建存储组(数据库)
     *
     * @param database 存储组名称(storageGroup /数据库)
     */
    private void checkStorageGroupOrDataBase(String database) throws IoTDBConnectionException,
            StatementExecutionException {
        // 在IoTDB中,数据库是通过存储组实现的,必须以root开头
        if (!database.startsWith("root.")) {
            database = "root." + database;
        }

        // 检查存储组是否已存在,如果不存在则创建
        if (storageGroupNoExists(database)) {
            session.setStorageGroup(database);
            logger.info("创建数据库(存储组): {}", database);
        } else {
            logger.info("数据库(存储组)已存在: {}", database);
        }
    }

    /**
     * 检查存储组是否存在
     *
     * @param storageGroup 存储组名称
     * @return false表示存储组已存在,true表示存储组不存在
     */
    private boolean storageGroupNoExists(String storageGroup) {
        try {
            SessionDataSet dataSet = session.executeQueryStatement("SHOW STORAGE GROUP");
            while (dataSet.hasNext()) {
                // 通常SHOW STORAGE GROUP的返回结果中第一列是存储组名称
                String existingGroup = dataSet.next().getFields().getFirst().toString();
                if (existingGroup.equals(storageGroup)) {
                    dataSet.closeOperationHandle();
                    return false;
                }
            }
            dataSet.closeOperationHandle();
        } catch (Exception e) {
            logger.warn("检查存储组是否存在时出错: {}", e.getMessage());
            // 如果查询失败,我们无法确定存储组是否存在,但可以尝试直接创建
            // 这里返回true让调用者尝试创建,如果存在则会收到异常
            return true;
        }
        return true;
    }

    /**
     * 规范化设备ID,确保其符合IoTDB路径规范 (root.storageGroup.device)
     * 如果设备ID只有两段 (如 root.001),则将其转换为三段 (root.default.001)
     * 同时处理路径组件中的特殊字符
     *
     * @param deviceId 原始设备ID
     * @return 符合规范的设备ID
     */
    private String normalizeDeviceId(String deviceId) {
        // 如果设备ID不以root开头,添加root前缀
        if (!deviceId.startsWith("root.")) {
            deviceId = "root." + deviceId;
        }

        // 分割路径以检查层级
        String[] parts = deviceId.split("\\.");

        // 如果只有两段 (如 root.001),添加默认的存储组名称作为第三段
        if (parts.length == 2) {
            String deviceName = parts[1];
            // 转义可能包含特殊字符的组件
            deviceName = escapeNodeName(deviceName);
            deviceId = "root.default." + deviceName;
            // 确保默认存储组存在
            try {
                checkStorageGroupOrDataBase("default");
            } catch (IoTDBConnectionException | StatementExecutionException e) {
                logger.error("创建默认存储组失败: {}", e.getMessage());
            }
        } else if (parts.length == 3) {
            // 如果已经有三层结构,检查存储组是否存在并转义组件
            String storageGroupName = parts[1];
            String deviceName = parts[2];

            // 转义可能包含特殊字符的组件
            storageGroupName = escapeNodeName(storageGroupName);
            deviceName = escapeNodeName(deviceName);

            deviceId = "root." + storageGroupName + "." + deviceName;

            try {
                checkStorageGroupOrDataBase(storageGroupName);
            } catch (IoTDBConnectionException | StatementExecutionException e) {
                logger.error("创建存储组 {} 失败: {}", storageGroupName, e.getMessage());
            }
        } else if (parts.length > 3) {
            // 处理更深层级的路径,转义所有组件
            StringBuilder sb = new StringBuilder("root.");
            for (int i = 1; i < parts.length; i++) {
                if (i > 1) sb.append(".");
                sb.append(escapeNodeName(parts[i]));
            }
            deviceId = sb.toString();

            // 确保存储组存在(第二段是存储组名)
            String storageGroupName = escapeNodeName(parts[1]);
            try {
                checkStorageGroupOrDataBase(storageGroupName);
            } catch (IoTDBConnectionException | StatementExecutionException e) {
                logger.error("创建存储组 {} 失败: {}", storageGroupName, e.getMessage());
            }
        }

        return deviceId;
    }

    /**
     * 转义IoTDB路径节点名中的特殊字符
     *
     * @param nodeName 原始节点名
     * @return 转义后的节点名
     */
    private String escapeNodeName(String nodeName) {
        // 检查节点名是否需要转义
        // IoTDB路径节点名应由字母、数字、下划线组成
        // 如果包含其他字符(如点号、连字符等),需要用反引号括起来
        if (nodeName == null || nodeName.isEmpty()) {
            return nodeName;
        }

        // 检查是否包含特殊字符
        if (!nodeName.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
            // 如果包含特殊字符,用反引号括起来
            // 先转义已有的反引号
            nodeName = nodeName.replace("`", "``");
            return "`" + nodeName + "`";
        }

        // 检查是否以数字开头
        if (nodeName.length() > 0 && Character.isDigit(nodeName.charAt(0))) {
            // 数字开头的节点名需要用反引号括起来
            return "`" + nodeName + "`";
        }

        return nodeName;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sanduo112

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值