TDengine 官方没有windows下的dll库,需要自行编译源码。
在linux下安装之后有现成so库,可以供jdbc的dirver连接需要使用。
TDengine 和springboot集成,如果使用mybaties,查询会出错,没有数据。
但是处理TDengine的数据一般只有新增和查询操作。
代码参见 https://github.com/yz4322gly/TDengineUtil.git
封装代码如下:
package cn.netuo.util;
import com.taosdata.jdbc.TSDBDriver;
import net.sf.cglib.beans.BeanMap;
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;
/**
* @author guolinyuan
*/
public class TDengineUtil
{
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 TDengineUtil(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 TDengineUtil(Connection connection, boolean databaseColumnHumpToLine)
{
this.connection = connection;
this.databaseColumnHumpToLine = databaseColumnHumpToLine;
}
/**
* 执行sql(无论是否返回结果),将结果注入到指定的类型实例中,且返回
* 当查询到的数据大于一个时,取第一个
*
* 对象遵从以下说明
* 1.对象字段为String类型,数据库类型(通过jdbc读取到的)无论什么类型,都将调用Object.toString方法注入值
* 2.对象字段为数据库类型(通过jdbc读取到的)一致的情况下,将会直接注入
* 3.对象字段与数据库类型(通过jdbc读取到的)不一致的情况下,将尝试使用{@link Class#cast(Object)}方法转型,失败此值会是类型默认值(故实体推荐使用封装类型)
* 4.对象字段为{@link Date}时,数据库类型为Date才可以注入,如果为long(例如TDengine)将会被当作毫秒的时间戳注入
*
* @param sql 要执行的sql
* @param clazz 要注入的实体类型
* @param 要注入的实体类型
* @return
* @throws IllegalAccessException
* @throws InstantiationException
* @throws SQLException
*/
public T getOne(String sql, Class 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;
*
* 对象遵从以下说明
* 1.对象字段为String类型,数据库类型(通过jdbc读取到的)无论什么类型,都将调用Object.toString方法注入值
* 2.对象字段为数据库类型(通过jdbc读取到的)一致的情况下,将会直接注入
* 3.对象字段与数据库类型(通过jdbc读取到的)不一致的情况下,将尝试使用{@link Class#cast(Object)}方法转型,失败此值会是类型默认值(故实体推荐使用封装类型)
* 4.对象字段为{@link Date}时,数据库类型为Date才可以注入,如果为long(例如TDengine)将会被当作毫秒的时间戳注入
*
* @param sql 要执行的sql
* @param clazz 要注入的实体类型
* @param 要注入的实体类型
* @return
* @throws IllegalAccessException
* @throws InstantiationException
* @throws SQLException
*/
public List getList(String sql, Class clazz) throws IllegalAccessException, InstantiationException, SQLException
{
List 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
{
Class clazz = o.getClass();
Map map = BeanMap.create(o);
String sql = createInsertSql(tableName,map);
return connection.createStatement().execute(sql);
}
/**
* 生成插入sql语句
* @param tableName
* @param map
* @return
*/
public static String createInsertSql(String tableName,Map map)
{
StringBuilder buffer = new StringBuilder();
buffer.append("INSERT INTO ").append(tableName).append(" (");
Set> set = map.entrySet();
StringBuilder keys = new StringBuilder(" ");
StringBuilder value = new StringBuilder(" ");
for (Map.Entry 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);
buffer.append(keys).append(") VALUES( ").append(value).append(")");
return buffer.toString();
}
/**
* 将resultSet注入到指定的类型实例中,且返回
* 对象遵从以下说明
* 1.对象字段为String类型,数据库类型(通过jdbc读取到的)无论什么类型,都将调用Object.toString方法注入值
* 2.对象字段为数据库类型(通过jdbc读取到的)一致的情况下,将会直接注入
* 3.对象字段与数据库类型(通过jdbc读取到的)不一致的情况下,将尝试使用{@link Class#cast(Object)}方法转型,失败此值会是类型默认值(故实体推荐使用封装类型)
* 4.对象字段为{@link Date}时,数据库类型为Date才可以注入,如果为long(例如TDengine)将会被当作毫秒的时间戳注入
*
* 注意,此方法只会注入一个结果,不会循环{@link ResultSet#next()}方法,请从外部调用。
* 传入setterMethods的目的是为了方便外部循环使用此方法,这样方法内部不会重复调用,提高效率
*
* @param resultSet 查询结果,一定要是{@link ResultSet#next()}操作过的,不然没有数据
* @param setterMethods clazz对应的所有setter方法,可以使用{@link this#getSetterMethods(Class)}获取
* @param clazz 注入对象类型
* @param 注入对象类型
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
public T resultSetToObject(ResultSet resultSet, Method[] setterMethods, Class 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 getSetterMethodsMap(Class clazz)
{
Method[] methods = clazz.getMethods();
Map 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 cn.netuo.util;
import java.util.List;
public class Main
{
public static void main(String[] args) throws Exception
{
TDengineUtil util = new TDengineUtil("jdbc:TAOS://127.0.0.1:6020/netuo_iot","root","taosdata",true);
// util.insert("v1_001",new cn.netuo.util.IotReceiptUploadDataEntity(new Date(),null,"33.3",true,"12","1234",12,"22.3","30","14",new Date()));
List list = util.getList("select * from v1_001",IotReceiptUploadDataEntity.class);
System.out.println(list);
}
}
实体类如下
package cn.netuo.util;
import java.util.Date;
/**
* @author guolinyuan
*/
public class IotReceiptUploadDataEntity
{
/**
* lng : 经纬度
* lat : 经纬度
* isCharging : 电池是否在充电
* battery : 电池电量
* workingTime : 连续开机时间
* ei : 业务电流(A)
* ep : 业务功率(W)
* tmp : 温度
* humidity : 湿度
* downtime : 上次停机时间,时间戳,精确到毫秒的(本文时间戳均为精确到毫秒的)
*/
private Date ts;
private String lng;
private String lat;
private Boolean isCharging;
private String battery;
private String workingTime;
private Integer ei;
private String ep;
private String tmp;
private String humidity;
private Date downtime;
public IotReceiptUploadDataEntity()
{
}
public IotReceiptUploadDataEntity(Date ts, String lng, String lat, Boolean isCharging, String battery, String workingTime, Integer ei, String ep, String tmp, String humidity, Date downtime)
{
this.ts = ts;
this.lng = lng;
this.lat = lat;
this.isCharging = isCharging;
this.battery = battery;
this.workingTime = workingTime;
this.ei = ei;
this.ep = ep;
this.tmp = tmp;
this.humidity = humidity;
this.downtime = downtime;
}
public void setEi(Integer ei)
{
this.ei = ei;
}
public Date getTs()
{
return ts;
}
public void setTs(Date ts)
{
this.ts = ts;
}
public String getLng()
{
return lng;
}
public void setLng(String lng)
{
this.lng = lng;
}
public String getLat()
{
return lat;
}
public void setLat(String lat)
{
this.lat = lat;
}
public Boolean getIsCharging()
{
return isCharging;
}
public void setIsCharging(Boolean isCharging)
{
this.isCharging = isCharging;
}
public String getBattery()
{
return battery;
}
public void setBattery(String battery)
{
this.battery = battery;
}
public String getWorkingTime()
{
return workingTime;
}
public void setWorkingTime(String workingTime)
{
this.workingTime = workingTime;
}
public String getEp()
{
return ep;
}
public void setEp(String ep)
{
this.ep = ep;
}
public String getTmp()
{
return tmp;
}
public void setTmp(String tmp)
{
this.tmp = tmp;
}
public String getHumidity()
{
return humidity;
}
public void setHumidity(String humidity)
{
this.humidity = humidity;
}
public Date getDowntime()
{
return downtime;
}
public void setDowntime(Date downtime)
{
this.downtime = downtime;
}
@Override
public String toString()
{
return "cn.netuo.util.IotReceiptUploadDataEntity{" +
"ts=" + ts +
", lng='" + lng + '\'' +
", lat='" + lat + '\'' +
", isCharging='" + isCharging + '\'' +
", battery='" + battery + '\'' +
", workingTime='" + workingTime + '\'' +
", ei='" + ei + '\'' +
", ep='" + ep + '\'' +
", tmp='" + tmp + '\'' +
", humidity='" + humidity + '\'' +
", downtime='" + downtime + '\'' +
'}';
}
}