springCloud集成tdengine(原生和mapper方式) 其二 原生篇
mapper篇请看另一篇文章
一、引入pom文件
<!-- TDengine 连接器--><dependency><groupId>com.taosdata.jdbc</groupId><artifactId>taos-jdbcdriver</artifactId><version>3.5.3</version></dependency>
二、在nacos中填写数据库各种value值
tdengine: datasource:location: yourLocationusername: rootpassword: yourPassword
三、编写TDengineUtil文件
下方util文件里面,包含创建database的方法,save方法以及getList方法,还有查询单个的方法。有需要的可以自行选择。这个包已经经过测试过了,有问题欢迎批评指正。在底部我会附上其他代码,是在其他地方copy的,具体使用方法没试过。大家自行选择。
package com.apex.iot.utils;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.*;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** TDengine的util类* createDate: 2025.03.11* 使用方法:* 1 需要new TDengine,创建链接 connection* 2 如果有需要自己写的有关的sql,直接调用useOrInsertSql即可* 例如:* TDengineUtil util1 = new TDengineUtil();* util1.useOrInsertSql(str.toString());* 上述操作即可执行有关新增或修改的sql语句。**/
@Slf4j
public class TDengineUtil {private static Connection connection;private static Pattern humpPattern = Pattern.compile("[A-Z]");private static Statement statement;/*** new的时候创建好链接* @param jdbcUrl 要连接的数据库地址*/public TDengineUtil(String jdbcUrl) throws SQLException {if(this.connection == null){Properties properties = new Properties();properties.setProperty("charset", "UTF-8");properties.setProperty("locale", "en_US.UTF-8");properties.setProperty("timezone", "UTC-8");try {this.connection = DriverManager.getConnection(jdbcUrl, properties);this.statement = this.connection.createStatement();System.out.println("Connected to " + jdbcUrl + " successfully.");}catch (Exception e){e.printStackTrace();System.out.println("Connect to " + jdbcUrl + " failed.");}}}public static void closeConnection(){// 手动关闭资源if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 创建database* @param databaseName 必填*/public void createDatabase(String databaseName) throws SQLException {if(!StringUtils.isBlank(databaseName)){statement.executeUpdate("CREATE DATABASE IF NOT EXISTS " + databaseName);System.out.println("Database created successfully.");}}/*** 使用或insert或修改的烧起来语句* @param sql sql语句*/public void useOrInsertSql(String sql) throws SQLException {if(!StringUtils.isBlank(sql)){log.info("TDengine执行sql语句:{}", sql);int a = statement.executeUpdate(sql);log.info("执行成功! 更改行数:{}", a);}}/*** 执行sql(无论是否返回结果),将结果注入到指定的类型实例中,且返回* 当查询到的数据大于一个时,取第一个* <p>* 对象遵从以下说明<br/>* 1.对象字段为String类型,数据库类型(通过jdbc读取到的)无论什么类型,都将调用Object.toString方法注入值<br/>* 2.对象字段为数据库类型(通过jdbc读取到的)一致的情况下,将会直接注入<br/>* 3.对象字段与数据库类型(通过jdbc读取到的)不一致的情况下,将尝试使用{@link Class#cast(Object)}方法转型,失败此值会是类型默认值(故实体推荐使用封装类型)<br/>* 4.对象字段为{@link Date}时,数据库类型为Date才可以注入,如果为long(例如TDengine)将会被当作毫秒的时间戳注入<br/>* @param sql 要执行的sql* @param clazz 要注入的实体类型* @param <T> 要注入的实体类型* @param databaseColumnHumpToLine 是否需要下划线转驼峰*/public <T> T getOne(String sql, Class<T> clazz,boolean databaseColumnHumpToLine) throws IllegalAccessException, InstantiationException, SQLException {ArrayList<Method> methods = getSetterMethods(clazz);log.info("TDengine执行sql语句:{}", sql);ResultSet resultSet = statement.executeQuery(sql);log.info("执行成功,查询到的数据:",resultSet);//只有一个结果直接下一个就行resultSet.next();return resultSetToObject(resultSet, methods, clazz,databaseColumnHumpToLine);}/*** 执行sql(无论是否返回结果),将结果注入到指定的类型实例中,且返回* @param sql 要查询的sql语句* @param clazz 方法类* @param databaseColumnHumpToLine 是否启用驼峰转下划线* @param <T> 要注入的实体类型* @throws Exception*/public <T> List<T> getList(String sql,Class<T> clazz,boolean databaseColumnHumpToLine) throws Exception{List<T> list = new ArrayList<>();log.info("TDengine执行sql语句:{}", sql);try(ResultSet resultSet = statement.executeQuery(sql);){log.info("执行成功,查询到的数据:",resultSet);// 获取所有的set方法,如果没有set方法,直接返回为空ArrayList<Method> setterMethods = getSetterMethods(clazz);while (resultSet.next()) {list.add(resultSetToObject(resultSet, setterMethods, clazz,databaseColumnHumpToLine));}return list;}catch (Exception e){throw e;}}/*** 通过setter method,获取到其对应的属性名** @param method* @return*/public static String getFieldNameBySetter(Method method){return toLowerCaseFirstOne(method.getName().substring(3));}/*** 首字母转小写*/public static String toLowerCaseFirstOne(String s){if (Character.isLowerCase(s.charAt(0))){return s;}else{return Character.toLowerCase(s.charAt(0)) + s.substring(1);}}/**** @param databaseColumnHumpToLine 是否需要启动下划线转驼峰*/public <T> T resultSetToObject(ResultSet resultSet, List<Method> setterMethods, Class<T> clazz,boolean databaseColumnHumpToLine) throws IllegalAccessException, InstantiationException {T result;try {result = clazz.newInstance();}catch (InstantiationException e) {System.out.println("请检查类" + clazz.getCanonicalName() + "是否有无参构造方法");throw e;}for (Method method : setterMethods) {try {String fieldName = getFieldNameBySetter(method);//因为标准的setter方法只会有一个参数,所以取一个就行了Class getParamClass = method.getParameterTypes()[0];//获得查询的结果Object resultObject;//是否启用驼峰转下划线规则获得数据库字段名resultObject = this.ResultSetGetObject(resultSet,databaseColumnHumpToLine,getParamClass,fieldName);//如果实体类的类型是String类型,那么无论x数据库类型是什么,都调用其toString方法获取值if (getParamClass.equals(String.class)) {method.invoke(result, resultObject.toString());}else if (getParamClass.equals(Date.class) && resultObject.getClass().equals(Long.class)) {method.invoke(result, new Date((Long) resultObject));} else {try {method.invoke(result, resultObject);} catch (IllegalArgumentException e) {//对象字段与数据库类型(通过jdbc读取到的)不一致的情况下,将尝试强制转型method.invoke(result, getParamClass.cast(resultObject));}}}catch (Exception ignored) {//所有的转型都失败了,则使用默认值}}return result;}public static Object ResultSetGetObject(ResultSet resultSet,Boolean humToLine,Class paramClass,String fieldName) throws SQLException {if(humToLine){fieldName = humpToLine(fieldName);}Object result = null;// 如果是String类型的,通过ResultSet.getObject根本获取不到数据,获取的会乱码,所以我们要根据ResultSet.getString进行获取if(paramClass.equals(String.class)){result = resultSet.getString(fieldName);}else{result = resultSet.getObject(fieldName);}return result;}/*** 获取指定类型方法的所有的setter方法*/public static ArrayList<Method> getSetterMethods(Class clazz){Method[] methods = clazz.getMethods();ArrayList<Method> setterMothodsList = new ArrayList<>();for (Method m : methods) {if (m.getName().startsWith("set")) {setterMothodsList.add(m);}}if(setterMothodsList.size()>0){return setterMothodsList;}return null;}/*** 往表里添加一个对象* @param tableName 必填 table名称* @param obj 必填,要添加的对象*/public void insert(String tableName,Object obj) throws SQLException {if(!StringUtils.isBlank(tableName) && null != obj){Map<String, Object> map = MapUtil.objectToMap(obj);StringBuilder builder = new StringBuilder();builder.append("INSERT INTO ").append(tableName);builder.append(mapToSQL(map));String sql =builder.toString();System.out.println(sql);statement.executeUpdate(sql);}}public static String mapToSQL(Map<String, Object> map){StringBuilder builder = new StringBuilder();Set<Map.Entry<String, Object>> set = map.entrySet();StringBuilder keys = new StringBuilder(" ");StringBuilder value = new StringBuilder(" ");for (Map.Entry<String, Object> entry : set){keys.append(humpToLine(entry.getKey())).append(",");try{if (entry.getValue().getClass().equals(Date.class)){Date d = (Date) entry.getValue();value.append(d.getTime()).append(",");}else{value.append("'").append(entry.getValue()).append("'").append(",");}}catch (Exception ignored){}}keys.deleteCharAt(keys.length() - 1);value.deleteCharAt(value.length() - 1);builder.append(" (").append(keys).append(") VALUES( ").append(value).append(")");return builder.toString();}/*** 驼峰转下划线,效率比上面高*/public static String humpToLine(String str){Matcher matcher = humpPattern.matcher(str);StringBuffer sb = new StringBuffer();while (matcher.find()){matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());}matcher.appendTail(sb);return sb.toString();}}
下面是TDengineUtil的其他方法,有需要的可以看以下
package com.apex.iot.utils.utilModel;import com.apex.iot.utils.MapUtil;
import com.taosdata.jdbc.TSDBDriver;import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class TDengineUtilTest {private Connection connection;private boolean databaseColumnHumpToLine;/*** @param url url 例如 : "jdbc:TAOS://127.0.0.1:6020/netuo_iot"* @param username 例如: "root"* @param password 例如: "taosdata"* @param databaseColumnHumpToLine 是否需要数据库列名下划线转驼峰*/public TDengineUtilTest(String url, String username, String password, boolean databaseColumnHumpToLine) throws ClassNotFoundException, SQLException{Class.forName("com.taosdata.jdbc.TSDBDriver");String jdbcUrl = url;Properties connProps = new Properties();connProps.setProperty(TSDBDriver.PROPERTY_KEY_USER, username);connProps.setProperty(TSDBDriver.PROPERTY_KEY_PASSWORD, password);connProps.setProperty(TSDBDriver.PROPERTY_KEY_CONFIG_DIR, "/etc/taos");connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");this.connection = DriverManager.getConnection(jdbcUrl, connProps);this.databaseColumnHumpToLine = databaseColumnHumpToLine;}/*** @param connection* @param databaseColumnHumpToLine*/public TDengineUtilTest(Connection connection, boolean databaseColumnHumpToLine){this.connection = connection;this.databaseColumnHumpToLine = databaseColumnHumpToLine;}/*** 执行sql(无论是否返回结果),将结果注入到指定的类型实例中,且返回* 当查询到的数据大于一个时,取第一个* <p>* 对象遵从以下说明<br/>* 1.对象字段为String类型,数据库类型(通过jdbc读取到的)无论什么类型,都将调用Object.toString方法注入值<br/>* 2.对象字段为数据库类型(通过jdbc读取到的)一致的情况下,将会直接注入<br/>* 3.对象字段与数据库类型(通过jdbc读取到的)不一致的情况下,将尝试使用{@link Class#cast(Object)}方法转型,失败此值会是类型默认值(故实体推荐使用封装类型)<br/>* 4.对象字段为{@link Date}时,数据库类型为Date才可以注入,如果为long(例如TDengine)将会被当作毫秒的时间戳注入<br/>** @param sql 要执行的sql* @param clazz 要注入的实体类型* @param <T> 要注入的实体类型* @return* @throws IllegalAccessException* @throws InstantiationException* @throws SQLException*/public <T> T getOne(String sql, Class<T> clazz) throws IllegalAccessException, InstantiationException, SQLException{Method[] setterMethods = getSetterMethods(clazz);ResultSet resultSet = connection.createStatement().executeQuery(sql);//只有一个结果直接下一个就行resultSet.next();return resultSetToObject(resultSet, setterMethods, clazz);}/*** 执行sql(无论是否返回结果),将结果注入到指定的类型实例中,且返回* 当查询到的结果没有时,返回一个大小为0的list;* <p>* 对象遵从以下说明<br/>* 1.对象字段为String类型,数据库类型(通过jdbc读取到的)无论什么类型,都将调用Object.toString方法注入值<br/>* 2.对象字段为数据库类型(通过jdbc读取到的)一致的情况下,将会直接注入<br/>* 3.对象字段与数据库类型(通过jdbc读取到的)不一致的情况下,将尝试使用{@link Class#cast(Object)}方法转型,失败此值会是类型默认值(故实体推荐使用封装类型)<br/>* 4.对象字段为{@link Date}时,数据库类型为Date才可以注入,如果为long(例如TDengine)将会被当作毫秒的时间戳注入<br/>** @param sql 要执行的sql* @param clazz 要注入的实体类型* @param <T> 要注入的实体类型* @return* @throws IllegalAccessException* @throws InstantiationException* @throws SQLException*/public <T> List<T> getList(String sql, Class<T> clazz) throws IllegalAccessException, InstantiationException, SQLException{List<T> list = new ArrayList<>();Method[] setterMethods = getSetterMethods(clazz);ResultSet resultSet = connection.createStatement().executeQuery(sql);while (resultSet.next()){list.add(resultSetToObject(resultSet, setterMethods, clazz));}return list;}/*** 插入对象到指定的表里面** @param tableName* @param o* @return* @throws SQLException*/@SuppressWarnings("all")public boolean insert(String tableName, Object o) throws SQLException{Map<String, Object> map = MapUtil.objectToMap(o);String sql = createInsertSql(tableName, map);return connection.createStatement().execute(sql);}public boolean insertWithStable(String tableName, String sTableName, Object o, String... tags) throws SQLException{Map<String, Object> map = MapUtil.objectToMap(o);String sql = createInsertStableSql(tableName,sTableName, map,tags);return connection.createStatement().execute(sql);}/*** 生成插入Stable的sql语句** @param tableName* @param map* @return*/public static String createInsertStableSql(String tableName, String sTbaleName, Map<String, Object> map, String... tags){StringBuilder builder = new StringBuilder();builder.append("INSERT INTO ").append(tableName);builder.append(stableToSQL(sTbaleName, tags));builder.append(mapToSQL(map));return builder.toString();}private static String stableToSQL(String sTableName, String... tags){StringBuilder builder = new StringBuilder();builder.append(" using ").append(sTableName).append(" TAGS ( ");for (String tag : tags){builder.append("'").append(tag).append("',");}builder.deleteCharAt(builder.length() - 1).append(" ) ");return builder.toString();}/*** 生成插入sql语句** @param tableName* @param map* @return*/public static String createInsertSql(String tableName, Map<String, Object> map){StringBuilder builder = new StringBuilder();builder.append("INSERT INTO ").append(tableName);builder.append(mapToSQL(map));return builder.toString();}public static String mapToSQL(Map<String, Object> map){StringBuilder builder = new StringBuilder();Set<Map.Entry<String, Object>> set = map.entrySet();StringBuilder keys = new StringBuilder(" ");StringBuilder value = new StringBuilder(" ");for (Map.Entry<String, Object> entry : set){keys.append(humpToLine(entry.getKey())).append(",");try{if (entry.getValue().getClass().equals(Date.class)){Date d = (Date) entry.getValue();value.append(d.getTime()).append(",");}else{value.append("'").append(entry.getValue()).append("'").append(",");}}catch (Exception ignored){}}keys.deleteCharAt(keys.length() - 1);value.deleteCharAt(value.length() - 1);builder.append(" (").append(keys).append(") VALUES( ").append(value).append(")");return builder.toString();}/*** 将resultSet注入到指定的类型实例中,且返回* 对象遵从以下说明<br/>* 1.对象字段为String类型,数据库类型(通过jdbc读取到的)无论什么类型,都将调用Object.toString方法注入值<br/>* 2.对象字段为数据库类型(通过jdbc读取到的)一致的情况下,将会直接注入<br/>* 3.对象字段与数据库类型(通过jdbc读取到的)不一致的情况下,将尝试使用{@link Class#cast(Object)}方法转型,失败此值会是类型默认值(故实体推荐使用封装类型)<br/>* 4.对象字段为{@link Date}时,数据库类型为Date才可以注入,如果为long(例如TDengine)将会被当作毫秒的时间戳注入<br/>* <p>* 注意,此方法只会注入一个结果,不会循环{@link ResultSet#next()}方法,请从外部调用。<br/>* 传入setterMethods的目的是为了方便外部循环使用此方法,这样方法内部不会重复调用,提高效率<br/>** @param resultSet 查询结果,一定要是{@link ResultSet#next()}操作过的,不然没有数据* @param setterMethods clazz对应的所有setter方法,可以使用{@link this#getSetterMethods(Class)}获取* @param clazz 注入对象类型* @param <T> 注入对象类型* @return* @throws IllegalAccessException* @throws InstantiationException*/public <T> T resultSetToObject(ResultSet resultSet, Method[] setterMethods, Class<T> clazz) throws IllegalAccessException, InstantiationException{T result;try{result = clazz.newInstance();}catch (InstantiationException e){System.out.println("请检查类" + clazz.getCanonicalName() + "是否有无参构造方法");throw e;}for (Method method : setterMethods){try{String fieldName = getFieldNameBySetter(method);//因为标准的setter方法只会有一个参数,所以取一个就行了Class getParamClass = method.getParameterTypes()[0];//获得查询的结果Object resultObject;//是否启用驼峰转下划线规则获得数据库字段名if (databaseColumnHumpToLine){resultObject = resultSet.getObject(humpToLine(fieldName));}else{resultObject = resultSet.getObject(fieldName);}//如果实体类的类型是String类型,那么无论x数据库类型是什么,都调用其toString方法获取值if (getParamClass.equals(String.class)){method.invoke(result, resultObject.toString());}else if (getParamClass.equals(Date.class) && resultObject.getClass().equals(Long.class)){method.invoke(result, new Date((Long) resultObject));}else{try{method.invoke(result, resultObject);}catch (IllegalArgumentException e){//对象字段与数据库类型(通过jdbc读取到的)不一致的情况下,将尝试强制转型method.invoke(result, getParamClass.cast(resultObject));}}}catch (Exception ignored){//所有的转型都失败了,则使用默认值}}return result;}/*** 通过setter method,获取到其对应的属性名** @param method* @return*/public static String getFieldNameBySetter(Method method){return toLowerCaseFirstOne(method.getName().substring(3));}/*** 获取指定类型方法的所有的setter方法* 方法属性名为key,对应的方法为value** @param clazz* @return*/public static Map<String, Method> getSetterMethodsMap(Class clazz){Method[] methods = clazz.getMethods();Map<String, Method> setterMethods = new HashMap<>(methods.length / 2);for (Method m : methods){if (m.getName().startsWith("set")){setterMethods.put(toLowerCaseFirstOne(m.getName().substring(3)), m);}}return setterMethods;}/*** 获取指定类型方法的所有的setter方法** @param clazz* @return*/public static Method[] getSetterMethods(Class clazz){Method[] methods = clazz.getMethods();Method[] setterMethods = new Method[methods.length / 2];int i = 0;for (Method m : methods){if (m.getName().startsWith("set")){setterMethods[i] = m;i++;}}return setterMethods;}/*** 首字母转小写*/public static String toLowerCaseFirstOne(String s){if (Character.isLowerCase(s.charAt(0))){return s;}else{return Character.toLowerCase(s.charAt(0)) + s.substring(1);}}/*** 首字母转大写*/public static String toUpperCaseFirstOne(String s){if (Character.isUpperCase(s.charAt(0))){return s;}else{return Character.toUpperCase(s.charAt(0)) + s.substring(1);}}private static Pattern linePattern = Pattern.compile("_(\\w)");/*** 下划线转驼峰*/public static String lineToHump(String str){str = str.toLowerCase();Matcher matcher = linePattern.matcher(str);StringBuffer sb = new StringBuffer();while (matcher.find()){matcher.appendReplacement(sb, matcher.group(1).toUpperCase());}matcher.appendTail(sb);return sb.toString();}private static Pattern humpPattern = Pattern.compile("[A-Z]");/*** 驼峰转下划线,效率比上面高*/public static String humpToLine(String str){Matcher matcher = humpPattern.matcher(str);StringBuffer sb = new StringBuffer();while (matcher.find()){matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());}matcher.appendTail(sb);return sb.toString();}}
四、然后可以创建实体类了
package com.apex.iot.device.model;import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** 数据处理model* 数据在TDengine中* @CreateDate:2025-03-14*/
@Data
public class DeviceModel implements Serializable {// 时间 == 必须private Date createTime;// 设备名字/设备idprivate String deviceId;// 标签值private String ip;// 值private Object value;// 类型private String valueType;//采集状态private int status;// 设备名称private String deviceName;// 设备parentNameprivate String deviceParentName;// int类型的value值private Integer intValue;// 布尔类型的value值private Boolean booleanValue;// 小数类型的value值private Float floatValue;private Date ts;}
五、创建service。这个service中的方法用的第一种util中的方法。
@Service
@Slf4j
public class DeviceService {//TDengine中的superTable的名字private static final String superTable = "super_table";// databaseprivate static final String DATABASE = "database";// 连接地址、用户名、密码@Value("${tdengine.datasource.location}")private String host ;@Value("${tdengine.datasource.username}")private String username ;@Value("${tdengine.datasource.password}")private String password ;// 获取TDengine的utl方法public String getTDengineUrl(){return "jdbc:TAOS://" + host + ":6030/?user=" + username + "&password=" + password;}/*** 将数据存入TDengine中* 原生方法存储*/public void saveToTDengine(List<DeviceModel> deviceList) throws Exception{TDengineUtil util1 = new TDengineUtil(this.getTDengineUrl());// 先连接databaseutil1.useOrInsertSql("use " + DATABASE + ";");// 拼接sql字符串StringBuffer stringBuffer = new StringBuffer();stringBuffer.append("insert into ");for(DeviceModel deviceModel : deviceList){stringBuffer.append(deviceModel.getDeviceId());stringBuffer.append(" using ");stringBuffer.append(superTable);stringBuffer.append(" tags('");stringBuffer.append(deviceModel.getDeviceId());stringBuffer.append("',");if(StringUtils.isBlank(deviceModel.getDeviceName())){stringBuffer.append("null");}else{stringBuffer.append("'");stringBuffer.append(deviceModel.getDeviceName());stringBuffer.append("',");}if(StringUtils.isBlank(deviceModel.getDeviceParentName())){stringBuffer.append("null");}else{stringBuffer.append("'");stringBuffer.append(deviceModel.getDeviceParentName());stringBuffer.append("',");}if(StringUtils.isBlank(deviceModel.getIp())){stringBuffer.append("null");}else{stringBuffer.append("'");stringBuffer.append(deviceModel.getIp());stringBuffer.append("') values (");}stringBuffer.append(deviceModel.getCreateTime().getTime());stringBuffer.append(",");if(null == deviceModel.getIntValue()){stringBuffer.append("null,");}else{stringBuffer.append(deviceModel.getIntValue());stringBuffer.append(",");}if(null == deviceModel.getBooleanValue()){stringBuffer.append("null,");}else{stringBuffer.append(deviceModel.getBooleanValue());stringBuffer.append(",");}if(null == deviceModel.getFloatValue()){stringBuffer.append("null, ");}else {stringBuffer.append(deviceModel.getFloatValue());stringBuffer.append(", ");}stringBuffer.append(deviceModel.getStatus());stringBuffer.append(") ");}stringBuffer.append(";");System.out.println(stringBuffer.toString());// 执行sql语句,将数据存入数据库util1.useOrInsertSql(stringBuffer.toString());TDengineUtil.closeConnection();}/*** 在TDengine中获取设备信息*/public DeviceModel getDeviceMsgByTDengine(String deviceId) throws Exception {if(StringUtils.isBlank(deviceId)){return null;}TDengineUtil util = new TDengineUtil(this.getTDengineUrl());util.useOrInsertSql("use " + DATABASE + ";");String sql = "select ts,ip,device_parent_name,device_name,int_value,boolean_value,float_value,status from " + superTable + " where device_id = '"+deviceId+"' order by ts desc limit 1";log.info("打印的sql语句:{}", sql);DeviceModel device = util.getOne(sql,DeviceModel.class,true);TDengineUtil.closeConnection();if(null != device && null != device.getBooleanValue()){device.setValue(device.getBooleanValue());return device;}if(null != device && null != device.getFloatValue()){device.setValue(device.getFloatValue());return device;}if(null != device && null != device.getIntValue()){device.setValue(device.getIntValue());return device;}return device;
}
六、使用controller进行测试。这边就不编写了。
后来用了mapper方法,因为原生方法怕自己忘记close。
mapper方法在主页中《springCloud集成tdengine(原生和mapper方式) 其一》篇
相关文章:
springCloud集成tdengine(原生和mapper方式) 其二 原生篇
mapper篇请看另一篇文章 一、引入pom文件 <!-- TDengine 连接器--><dependency><groupId>com.taosdata.jdbc</groupId><artifactId>taos-jdbcdriver</artifactId><version>3.5.3</version></dependency>二、在nacos中填…...
gralloc usage flags
下面这些示例主要说明了 gralloc usage flags 在图像处理和多媒体应用中如何影响性能和正确性。让我们逐个详细分析每个问题的 根因 和 修复方案,并深入解析 gralloc 标志对 缓存管理 和 数据流 的影响。 ✅ Example 1: 长曝光快照耗时异常 📌 问题描述…...
RIP路由欺骗攻击与防御实验详解
一、基础网络配置 1. 路由器R1配置 interface GigabitEthernet0/0/0ip address 192.1.2.254 255.255.255.0 ! interface GigabitEthernet0/0/1ip address 192.1.3.254 255.255.255.0 ! router rip 1version 2network 192.1.2.0network 192.1.3.0 2. 路由器R2配置 interface…...
在 Linux下使用 Python 3.11 和 FastAPI 搭建带免费证书的 HTTPS 服务器
在当今数字化时代,保障网站数据传输的安全性至关重要。HTTPS 协议通过使用 SSL/TLS 加密技术,能够有效防止数据在传输过程中被窃取或篡改。本教程将详细介绍如何在 Ubuntu 22.04 系统上,使用 Python 3.11 和 FastAPI 框架搭建一个带有免费 SS…...
[QMT量化交易小白入门]-三十五、如何将聚宽策略信号转为QMT实盘交易
本专栏主要是介绍QMT的基础用法,常见函数,写策略的方法,也会分享一些量化交易的思路,大概会写100篇左右。 QMT的相关资料较少,在使用过程中不断的摸索,遇到了一些问题,记录下来和大家一起沟通,共同进步。 文章目录 相关阅读一、聚宽模拟运行:构建交易策略的基础在聚宽…...
国思RDIF低代码快速开发框架 v6.2版本发布
1、平台介绍 国思RDIF企业级低代码开发平台,给用户和开发者最佳的框架平台方案,为企业快速构建跨平台、企业级的应用提供强大支持。致力于解决企业信息化项目交付难、实施效率低、开发成本高的问题。能帮助企业快速构建美观易用、架构专业、安全可控的企…...
学习笔记 ASP.NET Core Web API 8.0部署到iis
一.修改配置文件 修改Program.cs配置文件将 if (app.Environment.IsDevelopment()) {app.UseSwagger();app.UseSwaggerUI(); }修改为 app.UseSwagger(); app.UseSwaggerUI(); 二.安装ASP.NET Core Runtime 8.0.14 文件位置https://dotnet.microsoft.com/en-us/download/do…...
【Linux】信号:产生信号
🔥个人主页:Quitecoder 🔥专栏:linux笔记仓 目录 01.信号信号处理简单理解信号的发送和保存由软件产生的信号由硬件(异常)产生的信号 01.信号 进程信号是操作系统提供的一种异步通知机制,用于…...
单片机自学总结
自从工作以来,一直努力耕耘单片机,至今,颇有收获。从51单片机,PIC单片机,直到STM32,以及RTOS和Linux,几乎天天在搞:51单片机,STM8S207单片机,PY32F003单片机,…...
Idea集成docker通过ca加密实现镜像打包
Idea集成docker实现镜像打包_ideadocker镜像打包-CSDN博客 之前通过这种方式虽然可以实现idea通过maven打jar包的同时把docker镜像也进行打包,但是这种方式存在很大漏洞,就是服务器的2375端口大开,任何人拿着idea通过这种方式都可以连…...
【Prometheus】prometheus标签替换label_replace,动态修改生成标签,增强查询的灵活性和表达能力
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…...
蓝桥杯 之 暴力回溯
文章目录 数字接龙小u的最大连续移动次数问题 在蓝桥杯中,十分喜欢考察对于网格的回溯的问题,对于这类的问题,常常会使用到这个DFS和BFS进行考察,不过无论怎么考察,都只是会在最基础的模本的基础上,根据这个…...
在K8S中挂载 Secret 到 Pod
在 Kubernetes 里,把 Secret 挂载到 Pod 中有两种主要方式:作为卷挂载和作为环境变量挂载。下面为你提供相应的代码示例。 作为卷挂载 Secret 将 Secret 作为卷挂载到 Pod 时,Secret 的每个键会成为挂载目录下的一个文件,文件内…...
LINUX网络编程API原型详细解析
1. 网络体系 1.1. 简介 网络采用分而治之的方法设计,将网络的功能划分为不同的模块,以分层的形式有机组合在一起。 每层实现不同的功能,其内部实现方法对外部其他层次来说是透明的。每层向上层提供服务,同时使用下层提供…...
Android 13 Launcher3最近任务列表“全部清除“按钮位置优化实战
一、问题背景与实现难点 在Android 13横屏设备开发中,系统默认将最近任务列表的"全部清除"按钮布局在屏幕左侧,这与用户习惯的底部布局存在明显差异。相较于Android 8.1时代SystemUI模块的实现,Android 13将相关逻辑迁移至Launche…...
docker最新源,及遇到问题+处理
目前国内可用Docker镜像源汇总(截至2025年3月) - CoderJia 遇到问题: Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp: lookup registry-1.docker.io on [::1]:53: read udp [::1]:13287->[:…...
Python简单爬虫实践案例
学习目标 能够知道Web开发流程 能够掌握FastAPI实现访问多个指定网页 知道通过requests模块爬取图片 知道通过requests模块爬取GDP数据 能够用pyecharts实现饼图 能够知道logging日志的使用 一、基于FastAPI之Web站点开发 1、基于FastAPI搭建Web服务器 # 导入FastAPI模…...
统信UOS中使用Vscode编程
写在前面:统信UOS其实就是套壳的Linux系统,所以有问题如果搜不到解决方法,可以参考Linux下的解决方法。 1.环境配置 Vscode : 1.85.0 Vscode就直接下载安装就行,然后安装插件:Volar、中文汉化包 node:18…...
C#从入门到精通(1)
目录 第一章 C#与VS介绍 第二章 第一个C#程序 (1)C#程序基本组成 1.命名空间 2.类 3.Main方法 4.注释 5.语句 6.标识符及关键字 (2)程序编写规范 1.代码编写规则 2.程序命名方法 3.元素命名规范 第三章 变量 &…...
openharmony中hilog实证记录说明(3.1和5.0版本)
每次用这个工具hilog都有一些小用法记不清,需要花一些时间去查去分析使用方法,为了给丰富多彩的生活留出更多的时间,所以汇总整理共享来了,它来了它来了~~~~~~~~~ 开始是想通过3.1来汇总的,但实际测试发现openharmony…...
飞书 设计智能字段:通过“字段类型”添加AI功能列
在飞书多维表格中,通过“字段类型”添加AI功能列的核心逻辑是将AI模型能力与结构化数据结合,实现自动化内容生成与信息处理。以下是具体操作步骤及关键要点,结合实际应用场景说明: 一、基础操作步骤 创建多维表格 登录飞书&#x…...
Cannot find module @rollup/rollup-win32-x64-msvc
方法1 在package.json中添加postinstall: "scripts": {"postinstall": "node -e \"const { platform } process; if (platform win32) { require(child_process).execSync(npm install rollup/rollup-win32-x64-msvc, { stdio: inherit });…...
Docker和Dify学习笔记
文章目录 1 docker学习1.1 基本命令使用1.1.1 docker ps查看当前正在运行的镜像1.1.2 docker stop停止容器1.1.3 docker compose容器编排1.1.4 docker网络[1] 进入到容器里面敲命令[2] docker network ls[3] brige网络模式下容器访问宿主机的方式 2 Dify的安装和基础使用2.1 下…...
【AIGC】Win10系统极速部署Docker+Ragflow+Dify
【AIGC】WIN10仅3步部署DockerRagflowDify 一、 Docker快速部署1.F2进入bios界面,按F7设置开启VMX虚拟化技术。保存并退出。2.打开控制面板配置开启服务3.到官网下载docker安装包,一键安装(全部默认勾选) 二、 RagFlow快速部署1.确…...
Oracle ASM 磁盘组冗余策略
Oracle ASM 磁盘组冗余策略 1. 外部冗余(External Redundancy)2. 普通冗余(Normal Redundancy)3. 高冗余(High Redundancy)关键注意事项如何选择合适的策略? Oracle ASM(Automatic S…...
C++ 数据结构
C++ 数据结构 概述 C++作为一种强大的编程语言,在软件开发领域有着广泛的应用。数据结构作为C++编程中不可或缺的一部分,它决定了程序的性能和效率。本文将详细介绍C++中的常见数据结构,包括其定义、特点以及在实际应用中的使用方法。 常见数据结构 1. 数组 数组是一种…...
Unity NodeCanvas AI使用笔记
扩展: 1. 输入输出参数限制,增加描述,根据接口判断类型限制 2.选择节点,遍历节点,行为节点 3.行为节点 行为执行的时候有互斥关系,加入一个queue,最后执行 4.NodeCanvas的参数传参可以由上个节点传到下个节…...
(* IOB = “FORCE“ *) 的使用分享
在Xilinx FPGA设计中,IOBFORCE是一个与输入输出块(IOB)相关的属性设置。这个设置主要用于控制逻辑是否被推入到IOB(Input/Output Block)中,即FPGA芯片边缘的I/O引脚附近的专用硬件资源。使用IOB属性可以帮助…...
【大语言模型_7】利用ragas框架评测rag系统指标
一、介绍 ragas是一个用来评估RAG系统的框架,允许不在依赖人工注释的情况下,通过一套指标评估检索模块和生成模块的性能及其质量。 二、准备 数据准备:需要准备评估数据集,数据集格式如下 [{"question": "安全智…...
adb常用的命令
1. 查看adb版本 adb version 2. 将apk安装包安装到手机/模拟器上 adb install apk路径 3. 获取apk包名和界面名 包名(package):决定程序的唯一性 界面名(activity):一个界面界面名,对应一个界面…...
手动集成sqlite的方法
注意到sqlite有backup方法(https://www.sqlite.org/backup.html)。 也注意到android中sysroot下,没有sqlite3的库,也没有相关头文件。 如果要使用 sqlite 的backup,那么就需要手动集成sqlite代码到项目中。可以如下操…...
自然语言处理(NLP)技术
人工智能(Artificial Intelligence,AI)是一种模拟人类智能思维过程的技术,它在现代科技中的应用非常广泛,涉及诸多领域,如自然语言处理、计算机视觉、机器学习、数据分析等。以下是人工智能在现代科技中的应…...
【GPT入门】第25课 掌握 LangChain:链式调用的奥秘、特性与使用示例
【GPT入门】第25课 掌握 LangChain:链式调用的奥秘、特性与使用示例 语法解释各部分性质链式调用的性质调用方式注意事项 语法解释 你给出的代码 is_duplicated_chain (check_duplicated | model | parser) 运用了 LangChain 里的链式调用语法。在 LangChain 中&a…...
机器学习之DBSCAN算法详解
文章目录 引言1. DBSCAN算法概述2.DBSCAN算法的基本概念2.1 ε-邻域2.2 核心点(Core Point)2.3 边界点(Border Point)2.4 噪声点(Noise Point)2.5 直接密度可达(Directly Density-Reachable&…...
借助vite来优化前端性能
Vite 是一个现代化的前端构建工具,凭借其基于原生 ES 模块的开发服务器和高效的构建能力,可以显著优化前端性能。 一、开发环境优化 1.快速启动与热更新 Vite 利用浏览器对 ES 模块的原生支持,在开发环境中无需打包,直接按需加载…...
[工控机安全] 使用DriverView快速排查不可信第三方驱动(附详细图文教程)
导语: 在工业控制领域,设备驱动程序的安全性至关重要。第三方驱动可能存在兼容性问题、安全漏洞甚至恶意代码,威胁设备稳定运行。本文将手把手教你使用 DriverView工具,高效完成工控机驱动安全检查,精准识别可疑驱动&a…...
Execution failed for task ‘:path_provider_android:compileDebugJavaWithJavac‘.
What went wrong: Execution failed for task ‘:path_provider_android:compileDebugJavaWithJavac’. Could not resolve all files for configuration ‘:path_provider_android:androidJdkImage’. Failed to transform core-for-system-modules.jar to match attributes {…...
基于SpringBoot的社区/物业管理系统
项目介绍 平台采用B/S结构,后端采用主流的SpringBoot语言进行开发,前端采用主流的Vue.js进行开发。是一个综合的社区/物业管理系统。 整个平台包括前台和后台两个部分。 - 前台功能包括:小区信息、社区论坛、社区公告、社区留言板、个人中心。…...
vmware下linux无法上网解决方法
首先,打开打开"编辑" “虚拟网络编辑器”,并将"桥接"方式的网卡选择为主机上网的网卡。 虚拟机中,设置IP地址为主机网卡同样子网下的ip地址: 并且要选择桥接模式!注意如下图,"复制物理连接状…...
【数据库备份】docker中数据库备份脚本——MySql备份脚本
docker中数据库备份脚本——MySql备份脚本 #!/bin/bash# MySQL数据库信息 DB_USER"root" DB_PASSWORD"你的密码"# 备份保存主目录 BACKUP_ROOT"/data/data_backup/mysql"# 最多保留的备份日期文件夹数 MAX_DATE_FOLDERS15# 数组包含要备份的数据…...
SpringBoot 第二课(Ⅰ) 整合springmvc(详解)
目录 一、SpringBoot对静态资源的映射规则 1. WebJars 资源访问 2. 静态资源访问 3. 欢迎页配置 二、SpringBoot整合springmvc 概述 Spring MVC组件的自动配置 中央转发器(DispatcherServlet) 控制器(Controller) 视图解…...
centos家用笔记
改用阿里云yum源 因CentOS7已经停止维护,原有的yum源也无法使用,在国内,改用阿里云yum源是个方便的选择。 cd /etc/yum.repos.d/ mkdir backup mv Cent* backup wget http://mirrors.aliyun.com/repo/Centos-7.repo mv Centos-7.repo Cen…...
数据可视化(matplotlib)-------辅助图标的设置
目录 一、认识图表常用的辅助元素 坐标轴 二、设置坐标轴的标签、刻度范围和刻度标签 (一)、设置坐标轴的标签 1、xlabel()------设置x轴标签 2、ylabel()------设置y轴标签 (二) 、设置刻度范围和刻度标签 1、xlim()和ylim()函数分别可…...
15-双链表-双链表基本操作
题目 来源 827. 双链表 - AcWing题库 思路 此题我只想说,千万千万别漏了头结点和尾结点,不然根本查不出来是哪里出了问题,因为传入的k会有问题;最左边插入,相当于是在头结点的右边插入(也就是0号节点的右…...
HTTP和RPC的区别
RPC和 HTTP是两种常见的通信方式,它们在设计目标、使用场景和技术实现上有显著区别。以下是它们的详细对比: 1. 定义与核心思想 特性RPCHTTPRemote Procedure Call远程过程调用HyperText Transfer Protocol超文本传输协议定义一种协议或框架࿰…...
【Linux内核系列】:动静态库详解
🔥 本文专栏:Linux 🌸作者主页:努力努力再努力wz 💪 今日博客励志语录: 有些鸟儿是注定是关不住的,因为它们的每一片羽翼都沾满了自由的光辉 ★★★ 本文前置知识: 编译与链接的过程…...
【IROS 2025】CMU提出路径规划器PIPE:机器人探索效率提升14.6%,地图准确率提高9.3%!
在自主机器人探索未知环境的研究中,如何高效地规划路径、最大化信息获取,一直是一个核心问题。传统的方法往往仅在离散的路径点上计算信息增益,而缺乏对整个路径信息获取的综合考量,从而可能导致探索低效甚至错误的规划决策。近日…...
《笔记》Android 获取第三方应用及查看应用信息、apk大小、缓存、存储,以及第三方清除缓存
获取应用相关信息: PS:manifest标签中设置以下属性表示系统应用 android:process"system" android:sharedUserId"android.uid.system" //获取所有应用(非系统apk,有些应用获取不到) List<ApplicationInf…...
npm 安装 pnpm 的详细步骤及注意事项
一、安装步骤 1.全局安装 pnpm npm install -g pnpm2.验证安装 pnpm -v输出版本号即表示安装成功。 二、升级 pnpm 若已安装旧版本,可通过以下命令升级: npm install -g pnpmlatest三、配置镜像加速 设置淘宝镜像 pnpm config set registry http…...
大白话详细解读React框架的diffing算法
1. Diffing 算法是什么? Diffing 算法是 React 用来比较虚拟 DOM(Virtual DOM)树的一种算法。它的作用是找出前后两次渲染之间的差异(diff),然后只更新这些差异部分,而不是重新渲染整个页面。 …...