Spring Boot 与 TDengine 的深度集成实践(二)

创建数据模型

定义实体类

在完成数据库连接配置后,我们需要创建与 TDengine 表对应的 Java 实体类。实体类是 Java 对象与数据库表之间的映射,通过定义实体类,我们可以方便地在 Java 代码中操作数据库中的数据,实现数据的持久化和读取 。

假设我们在 TDengine 中创建了一个名为sensor_data的表,用于存储传感器数据,表结构如下:

复制代码

CREATE TABLE sensor_data (

id INT AUTO_INCREMENT PRIMARY KEY,

sensor_id VARCHAR(50),

value FLOAT,

timestamp TIMESTAMP

);

在这个表中,id是主键,自增长;sensor_id表示传感器 ID;value表示传感器采集到的值;timestamp表示数据采集的时间戳 。

对应的 Java 实体类SensorData可以定义如下,将其放在src/main/java/com/example/springboottdengineintegration/entity目录下:

复制代码

package com.example.springboottdengineintegration.entity;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.Table;

@Entity

@Table(name = "sensor_data")

public class SensorData {

@Id

private int id; // 主键

private String sensorId; // 传感器ID

private float value; // 传感器值

private long timestamp; // 时间戳

// Getters 和 Setters

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getSensorId() {

return sensorId;

}

public void setSensorId(String sensorId) {

this.sensorId = sensorId;

}

public float getValue() {

return value;

}

public void setValue(float value) {

this.value = value;

}

public long getTimestamp() {

return timestamp;

}

public void setTimestamp(long timestamp) {

this.timestamp = timestamp;

}

}

在这个实体类中,我们使用了 JPA(Java Persistence API)的注解来映射表结构和字段关系 。

注解说明

  • @Entity:表明该类是一个 JPA 实体类,用于将 Java 类与数据库表进行映射,告诉 Spring Data JPA 这个类对应的是数据库中的一张表,Spring Data JPA 会根据这个注解来创建和管理与该实体类相关的数据库操作 。
  • @Table(name = "sensor_data"):指定实体类对应的数据库表名,如果不使用该注解,默认会使用类名作为表名,但通过这个注解可以显式指定表名,确保与 TDengine 中实际的表名一致,这里指定为sensor_data,与 TDengine 中创建的表名对应 。
  • @Id:用于标识该实体类的主键字段,在数据库中,主键是唯一标识一条记录的字段,通过这个注解,JPA 可以识别出id字段是SensorData实体类对应的表中的主键,从而在进行数据操作时,根据主键进行数据的查询、更新、删除等操作 。
  • @GeneratedValue(在上述示例中未使用,但在实际场景中可能会用到):用于指定主键的生成策略,它有一个strategy属性,常用的生成策略有以下几种 :
    • GenerationType.AUTO:JPA 自动选择合适的策略,是默认选项,它会根据底层数据库的类型和配置来选择合适的主键生成方式,例如对于 MySQL 数据库,可能会选择自增长方式 。
    • GenerationType.IDENTITY:采用数据库 ID 自增长的方式来自增主键字段,这种方式适用于支持自增长主键的数据库,如 MySQL、SQL Server 等,在上述SensorData实体类中,如果我们希望id字段使用数据库自增长方式,可以添加如下注解:
复制代码

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private int id;

  • GenerationType.SEQUENCE:通过序列产生主键,需要与@SequenceGenerator注解配合使用,指定序列名,这种方式常用于支持序列的数据库,如 Oracle 。
  • GenerationType.TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植 。

通过创建实体类并使用 JPA 注解进行映射,我们建立了 Java 对象与 TDengine 数据库表之间的联系,为后续的数据访问和业务逻辑处理奠定了基础 。

编写 DAO 层

使用 JPA 或 MyBatis

在 Spring Boot 项目中,数据访问对象(DAO)层负责与数据库进行交互,执行数据的增删改查(CRUD)操作。我们可以使用 Spring Data JPA 或 MyBatis 来实现 DAO 层,它们都提供了便捷的方式来操作数据库,并且与 Spring Boot 的集成非常方便 。

使用 Spring Data JPA

Spring Data JPA 是 Spring Data 的一部分,它基于 JPA 规范,提供了一种基于接口的、简洁的方式来访问数据库,大大减少了样板代码的编写 。在前面创建的 Spring Boot 项目中,我们已经添加了spring-boot-starter-data-jpa依赖,现在可以开始编写基于 Spring Data JPA 的 DAO 层代码。

首先,在src/main/java/com/example/springboottdengineintegration/dao目录下创建一个接口,例如SensorDataRepository,用于操作SensorData实体类对应的数据表。这个接口需要继承JpaRepository,JpaRepository是 Spring Data JPA 提供的一个基础接口,它已经包含了一些常用的 CRUD 方法,如save(保存实体)、findById(根据 ID 查找实体)、findAll(查找所有实体)、delete(删除实体)等,我们可以直接使用这些方法,无需编写具体实现 。

复制代码

package com.example.springboottdengineintegration.dao;

import com.example.springboottdengineintegration.entity.SensorData;

import org.springframework.data.jpa.repository.JpaRepository;

public interface SensorDataRepository extends JpaRepository<SensorData, Integer> {

// 这里可以添加自定义查询方法,目前先使用JpaRepository提供的默认方法

}

在上述代码中:

  • SensorDataRepository接口继承自JpaRepository<SensorData, Integer>,其中SensorData是我们之前定义的实体类,Integer是实体类中主键的类型,这里主键id的类型是int,包装类为Integer 。通过继承JpaRepository,SensorDataRepository接口自动拥有了操作SensorData实体类对应数据表的基本 CRUD 功能 。
使用 MyBatis

MyBatis 是一个流行的持久层框架,它允许我们通过 XML 文件或注解来编写 SQL 语句,实现对数据库的灵活操作 。如果选择使用 MyBatis 来实现 DAO 层,首先需要在pom.xml文件中添加 MyBatis 和 TDengine JDBC 驱动的依赖(如果之前没有添加):

复制代码

<dependencies>

<!-- MyBatis Starter依赖 -->

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

<version>3.0.2</version>

</dependency>

<!-- TDengine JDBC驱动依赖,之前已添加,这里仅为展示完整性 -->

<dependency>

<groupId>com.taosdata.jdbc</groupId>

<artifactId>tdengine-jdbc</artifactId>

<version>3.0.4.0</version>

</dependency>

<!-- 其他依赖,如Spring Web、Spring Data JPA等 -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

</dependencies>

添加完依赖后,在src/main/java/com/example/springboottdengineintegration/dao目录下创建一个接口,例如SensorDataMapper:

复制代码

package com.example.springboottdengineintegration.dao;

import com.example.springboottdengineintegration.entity.SensorData;

import org.apache.ibatis.annotations.Mapper;

import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper

public interface SensorDataMapper {

@Select("SELECT * FROM sensor_data")

List<SensorData> findAll();

// 这里可以添加更多自定义SQL方法,如插入、更新、删除等

}

在上述代码中:

  • @Mapper注解用于将该接口标记为 MyBatis 的 Mapper 接口,Spring 会自动扫描并将其注册为一个 Bean,这样在其他组件中就可以通过依赖注入的方式使用它 。
  • @Select("SELECT * FROM sensor_data")注解表示这是一个查询方法,其值为具体的 SQL 查询语句,该方法用于查询sensor_data表中的所有数据,并返回一个SensorData类型的列表 。

同时,还需要在src/main/resources目录下创建mybatis/mapper文件夹(如果不存在),并在其中创建一个 XML 文件,例如SensorDataMapper.xml,用于编写更复杂的 SQL 语句(如果@Select注解中的简单 SQL 不能满足需求) 。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="com.example.springboottdengineintegration.dao.SensorDataMapper">

<!-- 这里可以定义更多复杂的SQL语句 -->

</mapper>

在application.properties或application.yml文件中,还需要配置 MyBatis 的相关属性,例如:

复制代码

# application.properties配置

mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

mybatis.type-aliases-package=com.example.springboottdengineintegration.entity

复制代码

# application.yml配置

mybatis:

mapper-locations: classpath:mybatis/mapper/*.xml

type-aliases-package: com.example.springboottdengineintegration.entity

上述配置中:

  • mybatis.mapper-locations指定了 MyBatis Mapper XML 文件的位置,这里配置为classpath:mybatis/mapper/*.xml,表示在src/main/resources/mybatis/mapper目录下查找所有 XML 文件 。
  • mybatis.type-aliases-package指定了实体类的包名,MyBatis 会自动为该包下的实体类创建别名,方便在 XML 文件中使用 。

自定义查询方法

在实际开发中,除了使用框架提供的默认 CRUD 方法外,我们往往还需要定义一些自定义的查询方法来满足特定的业务需求 。无论是使用 Spring Data JPA 还是 MyBatis,都支持自定义查询方法 。

Spring Data JPA 自定义查询方法

在 Spring Data JPA 中,我们可以通过在继承了JpaRepository的接口中定义符合特定命名规则的方法,或者使用@Query注解来实现自定义查询 。

  1. 通过方法命名规则创建查询:Spring Data JPA 会根据方法名自动解析并生成相应的查询语句。例如,我们要查询SensorData表中sensorId为指定值的数据,可以在SensorDataRepository接口中添加如下方法:
复制代码

List<SensorData> findBySensorId(String sensorId);

在这个方法中,findBy是固定的前缀,表示查询操作,后面跟着实体类的属性名sensorId,Spring Data JPA 会自动生成 SQL 语句来查询sensorId等于传入参数的记录 。

  1. 使用 @Query 注解创建查询:当方法命名规则无法满足复杂查询需求时,可以使用@Query注解来编写自定义的 JPQL(Java Persistence Query Language)或原生 SQL 语句 。例如,我们要查询SensorData表中value大于某个值且timestamp在某个时间范围内的数据,可以使用如下方法:
复制代码

import org.springframework.data.jpa.repository.Query;

import org.springframework.data.repository.query.Param;

import java.util.List;

public interface SensorDataRepository extends JpaRepository<SensorData, Integer> {

@Query("SELECT s FROM SensorData s WHERE s.value > :value AND s.timestamp BETWEEN :startTime AND :endTime")

List<SensorData> findByValueAndTimestampRange(@Param("value") float value, @Param("startTime") long startTime, @Param("endTime") long endTime);

}

在上述代码中:

  • @Query注解中的值是 JPQL 语句,SELECT s FROM SensorData s表示从SensorData实体(对应数据库表)中查询数据,s是别名 。
  • WHERE s.value > :value AND s.timestamp BETWEEN :startTime AND :endTime是查询条件,:value、:startTime和:endTime是占位符,对应方法参数中的@Param注解指定的参数名 。
  • @Param注解用于将方法参数与@Query注解中的占位符进行绑定,确保参数在查询语句中正确传递 。

如果需要使用原生 SQL 语句,可以在@Query注解中设置nativeQuery = true,例如:

复制代码

@Query(value = "SELECT * FROM sensor_data WHERE value >?1 AND timestamp BETWEEN?2 AND?3", nativeQuery = true)

List<SensorData> findByValueAndTimestampRangeUsingNativeSql(float value, long startTime, long endTime);

在这个方法中,?1、?2和?3是原生 SQL 中的占位符,按照参数顺序对应方法参数 。

MyBatis 自定义查询方法

在 MyBatis 中,我们可以在 Mapper 接口中使用注解或在 XML 文件中编写 SQL 语句来实现自定义查询 。

  1. 使用注解方式:在SensorDataMapper接口中,除了前面的@Select注解示例,还可以使用@Insert、@Update、@Delete等注解来实现插入、更新和删除操作 。例如,插入一条SensorData记录的方法可以定义如下:
复制代码

import org.apache.ibatis.annotations.Insert;

import org.apache.ibatis.annotations.Param;

public interface SensorDataMapper {

@Insert("INSERT INTO sensor_data (sensor_id, value, timestamp) VALUES (#{sensorId}, #{value}, #{timestamp})")

int insertSensorData(@Param("sensorId") String sensorId, @Param("value") float value, @Param("timestamp") long timestamp);

}

在上述代码中:

  • @Insert注解中的值是插入数据的 SQL 语句,#{sensorId}、#{value}和#{timestamp}是占位符,会被方法参数的值替换 。
  • @Param注解用于指定参数名,确保 SQL 语句中的占位符与方法参数正确对应 。
  1. 使用 XML 文件方式:在SensorDataMapper.xml文件中,可以编写更复杂的 SQL 语句 。例如,更新SensorData记录的 SQL 可以如下编写:
复制代码

<mapper namespace="com.example.springboottdengineintegration.dao.SensorDataMapper">

<update id="updateSensorData">

UPDATE sensor_data

SET value = #{value}, timestamp = #{timestamp}

WHERE id = #{id}

</update>

</mapper>

在上述 XML 代码中:

  • namespace属性指定了该 Mapper XML 文件对应的 Mapper 接口的全限定名,即com.example.springboottdengineintegration.dao.SensorDataMapper,用于将 XML 中的 SQL 语句与接口方法关联起来 。
  • <update>标签表示这是一个更新操作,id属性的值updateSensorData要与SensorDataMapper接口中定义的更新方法名一致 。
  • UPDATE sensor_data是 SQL 的更新语句部分,SET value = #{value}, timestamp = #{timestamp}表示要更新的字段和对应的值,WHERE id = #{id}是更新的条件,#{value}、#{timestamp}和#{id}是占位符,会在执行 SQL 时被传入的参数值替换 。

在SensorDataMapper接口中,需要定义对应的方法来调用这个 XML 中的 SQL:

复制代码

public interface SensorDataMapper {

int updateSensorData(SensorData sensorData);

}

这个方法的参数类型为SensorData实体类,MyBatis 会自动将实体类中的属性值与 XML 中 SQL 语句的占位符进行匹配和替换 。

通过以上方式,我们可以根据具体的业务需求,在 Spring Boot 与 TDengine 集成项目中灵活地编写 DAO 层的自定义查询方法,实现对 TDengine 数据库中时序数据的各种操作 。

相关推荐
秋野酱几秒前
基于SpringBoot酒店管理系统设计和实现(源码+文档+部署讲解)
java·spring boot·后端
MrWho不迷糊2 分钟前
Spring Boot 首笔交易慢问题排查与优化实践
spring boot·性能优化
狗蛋不是狗10 分钟前
Python 实现的运筹优化系统代码详解(0-1规划背包问题)
python·数学建模·背包问题·0-1规划·狗蛋不是狗
写代码的小王吧32 分钟前
【网络安全】 防火墙技术
java·python·安全·web安全·网络安全·docker
XiaoLeisj33 分钟前
【MyBatis】深入解析 MyBatis:关于注解和 XML 的 MyBatis 开发方案下字段名不一致的的查询映射解决方案
xml·java·spring boot·spring·java-ee·tomcat·mybatis
x66ccff37 分钟前
[特殊字符] Pandas 常用操作对比:Python 运算符 vs Pandas 函数
开发语言·python·pandas
小白的高手之路1 小时前
torch.nn中的非线性激活介绍合集——Pytorch中的非线性激活
人工智能·pytorch·python·深度学习·神经网络·机器学习·cnn
逆风优雅1 小时前
python 爬取网站图片的小demo
开发语言·python
码界筑梦坊1 小时前
基于Pyhon的京东笔记本电脑数据可视化分析系统
python·信息可视化·数据分析·毕业设计·电脑·销量预测
stevenzqzq1 小时前
kotlin中主构造函数是什么
开发语言·python·kotlin