tdengine-orm-boot-starter是一个基于 SpringBootJdbc 的半 ORM 框架,用于便捷操作 TDengine 数据,其设计参考了 MyBatisPlus
本项目是一个 Maven 多模块项目,包含以下模块:
| 模块 | 说明 | 使用场景 |
|---|---|---|
tdengine-orm-annotation |
轻量级注解模块 | 仅定义实体类的项目(无需 Spring) |
tdengine-orm-boot-starter |
完整的 Spring Boot Starter | Spring Boot 应用 |
仓库包含演示子模块 tdengine-orm-demo(以 Git submodule 方式引入)。如果需要本地跑 demo 或演示测试:
- 获取子模块代码:
git submodule update --init --recursive(或在已有仓库中执行同样指令同步最新代码)。 - 进入子模块运行:
cd tdengine-orm-demo && mvn clean package(或mvn test)。 - 使用 IntelliJ IDEA 打开主仓库时,如未自动识别 demo,需要手动 Import 该 Maven 项目(
tdengine-orm-demo/pom.xml)才能看到源码与测试。
- Spring Boot AutoConfigure 2.x:直接依赖
spring-boot-autoconfigure,复用条件装配、配置绑定等自动化能力(兼容 Spring Boot 2.7+) - Spring Boot Starter Data JDBC 2.x:引入
spring-boot-starter-data-jdbc(内部包含spring-jdbc),框架主要基于JdbcTemplate - 自研模块:
tdengine-orm-annotation提供实体注解模型,tdengine-orm-boot-starter负责自动配置与模板能力
Maven - 在 pom.xml 中添加:
<!-- TDengine ORM Boot Starter -->
<dependency>
<groupId>io.github.zephyrcicd</groupId>
<artifactId>tdengine-orm-boot-starter</artifactId>
<version>${tdengine-orm.version}</version> <!-- 请查看最新版本 -->
</dependency>
<!-- TDengine JDBC 驱动(必需) -->
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>${taos-jdbcdriver.version}</version> <!-- 请根据您的 TDengine 版本选择合适的驱动版本 -->
</dependency>Gradle Kotlin DSL - 在 build.gradle.kts 中添加:
dependencies {
// TDengine ORM Boot Starter
implementation("io.github.zephyrcicd:tdengine-orm-boot-starter:${tdengineOrmVersion}") // 请查看最新版本
// TDengine JDBC 驱动(必需)
implementation("com.taosdata.jdbc:taos-jdbcdriver:${taosJdbcdriverVersion}") // 请根据您的 TDengine 版本选择
}Gradle Groovy DSL - 在 build.gradle 中添加:
dependencies {
// TDengine ORM Boot Starter
implementation "io.github.zephyrcicd:tdengine-orm-boot-starter:${tdengineOrmVersion}" // 请查看最新版本
// TDengine JDBC 驱动(必需)
implementation "com.taosdata.jdbc:taos-jdbcdriver:${taosJdbcdriverVersion}" // 请根据您的 TDengine 版本选择
}如果您的项目只需要定义实体类(如独立的 API 模块),可以只引入轻量级的注解模块:
Maven
<dependency>
<groupId>io.github.zephyrcicd</groupId>
<artifactId>tdengine-orm-annotation</artifactId>
<version>${tdengine-orm.version}</version>
</dependency>Gradle
implementation("io.github.zephyrcicd:tdengine-orm-annotation:${tdengineOrmVersion}")💡 最新版本:请访问 Maven Central 或 GitHub Releases 查看最新版本 💡 TDengine JDBC 驱动:请参考 Maven Central - taos-jdbcdriver 选择与您的 TDengine 服务器版本兼容的驱动版本(如 3.2.5、3.6.3 等)
本框架不负责创建数据源,需要用户自行配置。推荐使用 Spring Boot 标准方式配置:
spring:
datasource:
url: jdbc:TAOS://localhost:6030/test
username: root
password: taosdata
driver-class-name: com.taosdata.jdbc.TSDBDriver
td-orm:
enabled: true
log-level: ERROR
page-size: 500 # 批量操作分页大小,默认500💡 注意:从 2.x 版本开始,框架专注于 ORM 功能,数据源管理由用户或专门的数据源 starter 负责。
使用 @TdTable 和 @TdTag 注解定义实体:
@TdTable("sensor_data")
public class SensorData {
@TdTag
private String deviceId;
private Double temperature;
private Long ts;
// getter/setter...
}在服务类中注入 TdTemplate 即可使用:
@Service
public class IoTDataService {
@Autowired
private TdTemplate tdTemplate;
public void saveData(SensorData data) {
tdTemplate.insert(data);
}
}DefaultTagNameStrategy 现在需要通过 Spring 依赖注入使用,不再支持直接 new 实例化。
变更原因: 新增 Tag 顺序自动对齐 DDL 定义功能,生成子表名时 tag 值顺序与 TDengine DDL 定义保持一致。
迁移方式:
// 旧用法 (不再支持)
DefaultTagNameStrategy<Entity> strategy = new DefaultTagNameStrategy<>();
tdTemplate.insert(strategy, entity);
// 新用法 - 通过 Spring DI 注入
@Autowired
private DefaultTagNameStrategy defaultTagNameStrategy;
public void save(Entity entity) {
tdTemplate.insert(defaultTagNameStrategy, entity);
}新增功能:
TagOrderCacheManager:缓存超级表的 tag 定义顺序,避免重复查询TdOrmConfig.getDatabaseName():从 JDBC URL 自动提取数据库名称- Tag 顺序自动与 TDengine DDL 定义对齐,确保子表名生成一致性
从 2.x 版本开始,本框架不再自动创建数据源,而是专注于 ORM 功能。用户需要自行配置 TDengine 数据源。
最简单的方式是使用 Spring Boot 的标准数据源配置:
spring:
datasource:
url: jdbc:TAOS://localhost:6030/test
username: root
password: taosdata
driver-class-name: com.taosdata.jdbc.TSDBDriver
td-orm:
enabled: true # 可选,默认为 true
log-level: ERROR # 日志级别:ERROR, WARN, INFO, DEBUG
page-size: 500 # 批量操作分页大小,默认 500
enable-ts-auto-fill: true # 是否启用 ts 字段自动填充,默认 truespring.datasource.url=jdbc:TAOS://localhost:6030/test
spring.datasource.username=root
spring.datasource.password=taosdata
spring.datasource.driver-class-name=com.taosdata.jdbc.TSDBDriver
td-orm.enabled=true
td-orm.log-level=ERROR
td-orm.page-size=500如果需要更精细的连接池控制,可以自定义 DataSource Bean:
@Configuration
public class TdengineDataSourceConfig {
@Bean
@Primary
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:TAOS://localhost:6030/test");
config.setUsername("root");
config.setPassword("taosdata");
config.setDriverClassName("com.taosdata.jdbc.TSDBDriver");
// 自定义连接池配置
config.setMaximumPoolSize(30);
config.setMinimumIdle(10);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
return new HikariDataSource(config);
}
}@Configuration
public class TdengineDataSourceConfig {
@Bean
@Primary
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:TAOS://localhost:6030/test");
dataSource.setUsername("root");
dataSource.setPassword("taosdata");
dataSource.setDriverClassName("com.taosdata.jdbc.TSDBDriver");
// 自定义连接池配置
dataSource.setInitialSize(10);
dataSource.setMaxActive(50);
dataSource.setMinIdle(10);
dataSource.setMaxWait(30000);
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestWhileIdle(true);
return dataSource;
}
}在你的服务类中注入和使用 TdTemplate:
@Service
public class IoTDataService {
@Autowired
private TdTemplate tdTemplate;
public void saveData(SensorData data) {
// 插入单条数据
tdTemplate.insert(data);
}
public List<SensorData> findData() {
// 查询数据
TdQueryWrapper<SensorData> wrapper = TdWrappers.queryWrapper(SensorData.class)
.selectAll()
.orderByDesc("ts")
.limit(100);
return tdTemplate.list(wrapper);
}
}如果您想查看完整的、可运行的使用案例,请参考我们的 Demo 项目:
Demo 项目特点:
- ✅ 15个完整的测试用例,覆盖所有核心功能
- ✅ 包含性能统计和吞吐量测试
- ✅ 演示 PARTITION BY 分区查询、时间窗口等高级功能
- ✅ 开箱即用,配置数据库连接后即可运行
- ✅ 代码简洁清晰,适合学习参考
通过运行 Demo 项目的测试用例,您可以快速了解 TdTemplate 的各种使用方式。
该框架提供三个核心注解来定义 TDengine 实体类:
用于映射实体类到 TDengine 表或超级表:
@TdTable("sensor_data") // 指定表名
public class SensorData {
// ...
}标记 TAG 字段(TDengine 的元数据列),用于子表分组和过滤:
@TdTag
private String deviceId; // TAG 字段字段列映射注解,支持多种配置:
@TdColumn(value = "temp", type = TdFieldTypeEnum.DOUBLE, length = 8)
private Double temperature;
@TdColumn(exist = false)
private String internalField; // 不参与 SQL 生成的内部字段@TdColumn 主要属性:
value:自定义列名(默认使用字段的下划线形式)type:指定 TDengine 字段类型(默认自动推断)length:字段长度,适用于 NCHAR、BINARY、VARCHAR 等类型exist:控制字段是否参与 SQL 生成(默认 true)comment:字段注释nullable:是否允许为空compositeKey:是否为复合主键(仅 TDengine 3.3+ 支持)
@TdTable("sensor_data")
public class SensorData {
@TdTag
private String deviceId;
@TdTag
@TdColumn(value = "location", length = 100)
private String location;
@TdColumn(value = "temp", type = TdFieldTypeEnum.DOUBLE)
private Double temperature;
private Double humidity;
private Long ts;
// getter/setter 方法...
}框架提供自动填充功能,默认会自动填充名为 ts 的时间戳字段。该功能默认开启,可以通过配置进行关闭。
td-orm:
enabled: true
log-level: ERROR
enable-ts-auto-fill: true # 是否启用ts字段自动填充,默认为true自动填充功能支持多种时间类型:
Long/long- 毫秒时间戳Date- Java日期类型LocalDateTime- Java 8日期时间类型LocalDate- Java 8日期类型Instant- Java 8时间戳类型
实体类中只需定义名为 ts 的字段,框架会在插入数据时自动填充:
@TdTable("sensor_data")
public class SensorData {
private Long ts; // 会自动填充为当前时间戳
@TdTag
private String deviceId;
private Double temperature;
private Double humidity;
// getters and setters
}如果需要自定义填充逻辑,可以实现 MetaObjectHandler 接口:
@Component
public class CustomMetaObjectHandler implements MetaObjectHandler {
@Override
public <T> void insertFill(T object) {
// 自定义填充逻辑
}
}框架提供了灵活的 SQL 拦截器机制,允许用户在 SQL 执行前后添加自定义逻辑,如日志记录、性能监控、审计等。
框架内置了 LoggingSqlInterceptor,会根据配置的日志级别自动记录 SQL 执行日志:
td-orm:
log-level: DEBUG # DEBUG/INFO 级别会输出 SQL 日志
enable-sql-interceptor: true # 是否启用 SQL 拦截器,默认 true实现 TdSqlInterceptor 接口并注册为 Spring Bean 即可添加自定义拦截逻辑:
@Component
public class AuditSqlInterceptor implements TdSqlInterceptor {
@Override
public boolean beforeExecute(TdSqlContext context) {
// SQL 执行前的逻辑
log.info("Executing SQL: {}", context.getSql());
return true; // 返回 true 继续执行,返回 false 中断执行
}
@Override
public void afterExecute(TdSqlContext context, Object result, Throwable ex) {
// SQL 执行后的逻辑
long duration = System.currentTimeMillis() - context.getStartTime();
log.info("SQL completed in {}ms", duration);
if (ex != null) {
log.error("SQL execution failed: {}", ex.getMessage());
}
}
@Override
public int getOrder() {
// 拦截器执行顺序,数值越小优先级越高
return 100;
}
}TdSqlContext 提供了 SQL 执行的完整上下文信息:
getSql()- 获取 SQL 语句getParams()- 获取 SQL 参数getSqlType()- 获取 SQL 类型(UPDATE/QUERY/QUERY_ONE)getStartTime()- 获取执行开始时间getResultClass()- 获取结果类型(查询时)getAttributes()- 获取自定义属性(可在拦截器间传递数据)
beforeExecute:按getOrder()从小到大顺序执行afterExecute:按getOrder()从大到小逆序执行(类似栈的 LIFO)
框架提供了灵活的类型处理器机制,用于 Java 类型与数据库类型之间的序列化和反序列化转换,类似于 MyBatis 的 TypeHandler。
框架默认注册了常用类型的处理器:
| 处理器 | Java 类型 | 说明 |
|---|---|---|
StringTypeHandler |
String | 字符串类型 |
IntegerTypeHandler |
Integer | 整数类型 |
LongTypeHandler |
Long | 长整型 |
DoubleTypeHandler |
Double | 双精度浮点 |
FloatTypeHandler |
Float | 单精度浮点 |
BooleanTypeHandler |
Boolean | 布尔类型 |
TimestampTypeHandler |
Timestamp | 时间戳类型 |
ByteArrayTypeHandler |
byte[] | 字节数组 |
JsonMapTypeHandler |
Map<String, Object> | JSON与Map互转 |
ObjectTypeHandler |
Object | 智能处理:基础类型直接存储,复杂对象序列化为JSON |
// JSON 类型处理器 - 将对象序列化为 JSON 存储
JsonTypeHandler<MyPojo> handler = new JsonTypeHandler<>(MyPojo.class);
// 枚举类型处理器 - 按 name 存储
EnumTypeHandler<Status> handler = new EnumTypeHandler<>(Status.class);
// 枚举类型处理器 - 按 ordinal 存储
EnumOrdinalTypeHandler<Status> handler = new EnumOrdinalTypeHandler<>(Status.class);
// List 类型处理器 - 序列化为 JSON 数组
ListTypeHandler<String> handler = new ListTypeHandler<>(String.class);@TdTable("sensor_data")
public class SensorData {
@TdTypeHandler(JsonTypeHandler.class)
private SensorConfig config; // 自动序列化为 JSON
@TdTypeHandler(EnumTypeHandler.class)
private DeviceStatus status; // 按枚举名称存储
}当需要根据 type 字段动态决定 dataJson 的反序列化类型时:
// 方式1:注解配置
public class Event {
private String type;
@TdPolymorphic(
typeField = "type",
mappings = {
@TypeMapping(type = "SENSOR", target = SensorData.class),
@TypeMapping(type = "ALARM", target = AlarmData.class)
},
defaultType = BaseData.class
)
private Object data;
}
// 方式2:Builder 方式
PolymorphicFieldHandler handler = PolymorphicFieldHandler.builder()
.typeColumn("type")
.dataColumn("data_json")
.register("SENSOR", SensorData.class)
.register("ALARM", AlarmData.class)
.defaultType(BaseData.class)
.build();如果项目中已有 MyBatis TypeHandler,可以直接复用,无需重复开发:
// 批量注册已有的 MyBatis TypeHandler
TypeHandlerRegistry.getInstance().fromMybatis(
new MyJsonTypeHandler(),
new MyEnumTypeHandler(Status.class),
new MyCustomTypeHandler()
);
// 指定 Java 类型注册
TypeHandlerRegistry.getInstance().fromMybatis(MyPojo.class, new MyPojoTypeHandler());
// 从 Spring 容器批量注册
@Autowired
private List<org.apache.ibatis.type.TypeHandler<?>> mybatisHandlers;
@PostConstruct
public void init() {
TypeHandlerRegistry.getInstance().fromMybatis(mybatisHandlers);
}💡 注意:复用 MyBatis TypeHandler 需要添加 MyBatis 依赖(已设为 optional)
继承 BaseTypeHandler<T> 实现自定义类型转换:
public class LocalDateTypeHandler extends BaseTypeHandler<LocalDate> {
public LocalDateTypeHandler() {
super(LocalDate.class);
}
@Override
protected void setNonNullParameter(PreparedStatement ps, int index, LocalDate parameter) throws SQLException {
ps.setDate(index, Date.valueOf(parameter));
}
@Override
protected LocalDate getNullableResult(ResultSet rs, String columnName) throws SQLException {
Date date = rs.getDate(columnName);
return date != null ? date.toLocalDate() : null;
}
@Override
protected LocalDate getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
Date date = rs.getDate(columnIndex);
return date != null ? date.toLocalDate() : null;
}
@Override
protected LocalDate convertFromSqlValue(Object sqlValue) {
if (sqlValue instanceof Date) {
return ((Date) sqlValue).toLocalDate();
}
return null;
}
}
// 注册自定义处理器
TypeHandlerRegistry.getInstance().register(new LocalDateTypeHandler());该 starter 会自动创建以下 Bean(基于用户提供的 DataSource):
tdengineJdbcTemplate- TDengine 专用的 JdbcTemplatetdengineNamedParameterJdbcTemplate- TDengine 专用的 NamedParameterJdbcTemplatetdTemplate- TDengine 数据访问模板类
💡 注意:从 2.x 版本开始,框架不再自动创建 DataSource,需要用户自行配置数据源。
如果需要禁用 TDengine ORM 的自动配置,可以在配置文件中设置:
td-orm:
enabled: false或者在启动类上排除自动配置:
@SpringBootApplication(exclude = {TdOrmAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}- 确保 TDengine 服务正在运行并且数据源配置正确
- 框架依赖用户提供的 DataSource,请确保已正确配置数据源
- 该 starter 与 Spring Boot 的自动配置兼容,不会冲突
本项目是一个 Maven 多模块项目,使用 Maven 进行构建与发布,常用命令如下:
# 编译所有模块
mvn clean compile
# 打包所有模块(跳过测试,测试需要 TDengine 数据库)
mvn clean package -DskipTests
# 安装到本地 Maven 仓库(本地开发需使用 skip-gpg profile 跳过 GPG 签名)
mvn clean install -DskipTests -Pskip-gpg
# 编译指定模块
mvn clean compile -pl tdengine-orm-annotation
mvn clean compile -pl tdengine-orm-boot-starter
# 查看依赖树
mvn dependency:tree💡 注意:本地开发安装时必须使用
-Pskip-gpg参数跳过 GPG 签名,否则会因缺少 GPG 密钥而失败。
我们非常欢迎开发者为 TDengine ORM Boot Starter 贡献代码!无论是:
- 🐛 报告问题 - 发现 Bug 请在 Issues 中提交
- 💡 功能建议 - 有好的想法欢迎在 Issues 中讨论
- 🔧 提交代码 - 欢迎提交 Pull Request 改进项目
- 📖 完善文档 - 帮助我们改进文档和示例