文章目录
前言
随着科技的快速发展,物联网已经变成了生活中处处存在的技术,物联网的存在必然会产生数据,而且物联网中的各种传感器数据量是大量的,时序性的,这就需要时序数据库,本实例使用的的是JFinal极速 web 开发框架,时序数据库IoTDB 是清华大学主导的 Apache 孵化项目,是一款专为物联网时序数据打造的数据库,提供数据采集、存储、分析的功能。
使用步骤
1.引入库
<!-- IoTDB时序数据库 --> <dependency> <groupId>org.apache.iotdb</groupId> <artifactId>iotdb-session</artifactId> <version>0.13.2</version> <exclusions> <exclusion> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </exclusion> <exclusion> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> </exclusion> </exclusions> </dependency>
2.JFinal自定义一个IoTDB的插件
public class IoTDBPlugin implements IPlugin {
private static final Logger log = Logger.getLogger(IoTDBPlugin.class);
protected String host;
protected int port;
protected List<String> nodeUrls;
protected int fetchSize=-1;
protected String username;
protected String password;
protected int thriftDefaultBufferSize;
protected int thriftMaxFrameSize;
protected boolean enableCacheLeader;
protected Version version;
protected Session session;
public IoTDBPlugin() {
}
public IoTDBPlugin(String host,int port) {
this.host=host;
this.port=port;
}
public IoTDBPlugin(List<String> nodeUrls) {
this.nodeUrls=nodeUrls;
}
public IoTDBPlugin(String host,int port,String username,String password,
int fetchSize,int thriftDefaultBufferSize,
int thriftMaxFrameSize,boolean enableCacheLeader,Version version) {
this.host=host;
this.port=port;
this.username=username;
this.password=password;
this.fetchSize=fetchSize;
this.thriftDefaultBufferSize=thriftDefaultBufferSize;
this.thriftMaxFrameSize=thriftMaxFrameSize;
this.enableCacheLeader=enableCacheLeader;
this.version=version;
}
@Override
public boolean start() {
if(this.host!=null)
{
// 指定一个可连接节点
session =
new Session.Builder()
.host(this.host)
.port(this.port)
.build();
}
else if(this.nodeUrls!=null)
{
// 指定多个可连接节点
session =
new Session.Builder()
.nodeUrls(this.nodeUrls)
.build();
}
else if(this.fetchSize!=-1)
{
// 其他配置项
session =
new Session.Builder()
.fetchSize(this.fetchSize)
.username(this.username)
.password(this.password)
.thriftDefaultBufferSize(this.thriftDefaultBufferSize)
.thriftMaxFrameSize(this.thriftMaxFrameSize)
.enableCacheLeader(this.enableCacheLeader)
.version(this.version)
.build();
}
else
{
// 全部使用默认配置
session = new Session.Builder().build();
}
try {
session.open();
log.info("iotdb连接成功~");
// 设置时区
session.setTimeZone("+08:00");
} catch (IoTDBConnectionException e) {
e.printStackTrace();
} catch (StatementExecutionException e) {
e.printStackTrace();
}
IoTDBUtils.AddSession(session);
return true;
}
@Override
public boolean stop() {
try {
this.session.close();
IoTDBUtils.close();
} catch (IoTDBConnectionException e) {
e.printStackTrace();
}
return false;
}
}
3.插件启动配套的工具类:IoTDBUtils
public class IoTDBUtils {
static String key;
public IoTDBUtils() {
}
/**
* root.adlot.
* @param _key
*/
public static IoTDBUtils use(String _key) {
key=_key;
return new IoTDBUtils();
}
private static final Logger log = Logger.getLogger(IoTDBUtils.class);
static Session session;
/**
* 初始化Session
* @param _session
*/
public static void AddSession(Session _session) {
if (_session == null) {
throw new IllegalArgumentException("IoTDB Session can not be null");
} else {
session = _session;
}
}
/**
* 关闭Session
* @throws IoTDBConnectionException
*/
public static void close() throws IoTDBConnectionException {
if (session != null) {
session.close();
}
}
/**
* 获取Session
* @return
*/
public static Session getSession() {
return session;
}
/**
* description: 带有数据类型的添加操作 - insertRecord没有指定类型
*
* @param * @param deviceId:节点路径如:root.a1eaKSRpRty.CA3013A303A25467
* time:时间戳
* measurementsList:物理量 即:属性
* type:数据类型: BOOLEAN((byte)0), INT32((byte)1),INT64((byte)2),FLOAT((byte)3),DOUBLE((byte)4),TEXT((byte)5),VECTOR((byte)6);
* valuesList:属性值 --- 属性必须与属性值一一对应
* @return
*/
public static void insertRecordType(String deviceId, Long time, List<String> measurementsList, TSDataType type, List<Object> valuesList) throws StatementExecutionException, IoTDBConnectionException, ServerException {
if (measurementsList.size() != valuesList.size()) {
throw new ServerException("measurementsList 与 valuesList 值不对应");
}
List<TSDataType> types = new ArrayList<>();
measurementsList.forEach(item -> {
types.add(type);
});
session.insertRecord(deviceId, time, measurementsList, types, valuesList);
}
/**
* description: 带有数据类型的添加操作 - insertRecord没有指定类型
*
* @param deviceId:节点路径如:root.a1eaKSRpRty.CA3013A303A25467
* @param time:时间戳
* @param measurementsList:物理量 即:属性
* @param valuesList:属性值 --- 属性必须与属性值一一对应
* @return
*/
public static void insertRecord(String deviceId, Long time, List<String> measurementsList, List<String> valuesList)
throws StatementExecutionException, IoTDBConnectionException, ServerException {
if (measurementsList.size() == valuesList.size()) {
session.insertRecord(deviceId, time, measurementsList, valuesList);
} else {
log.error("measurementsList 与 valuesList 值不对应");
}
}
/**
* description: 批量插入
*/
public static void insertRecords(List<String> deviceIdList, List<Long> timeList, List<List<String>> measurementsList, List<List<String>> valuesList) throws StatementExecutionException, IoTDBConnectionException, ServerException {
if (measurementsList.size() == valuesList.size()) {
session.insertRecords(deviceIdList, timeList, measurementsList, valuesList);
} else {
log.error("measurementsList 与 valuesList 值不对应");
}
}
/**
* description: 插入操作
*
* @param deviceId:节点路径如:root.a1eaKSRpRty.CA3013A303A25467
* @param time:时间戳
* @param schemaList: 属性值 + 数据类型 例子: List<MeasurementSchema> schemaList = new ArrayList<>(); schemaList.add(new MeasurementSchema("breath", TSDataType.INT64));
* @param maxRowNumber:
* @return
*/
public static void insertTablet(String deviceId, Long time, List<MeasurementSchema> schemaList, List<Object> valueList, int maxRowNumber) throws StatementExecutionException, IoTDBConnectionException {
Tablet tablet = new Tablet(deviceId, schemaList, maxRowNumber);
// 向iotdb里面添加数据
int rowIndex = tablet.rowSize++;
tablet.addTimestamp(rowIndex, time);
for (int i = 0; i < valueList.size(); i++) {
tablet.addValue(schemaList.get(i).getMeasurementId(), rowIndex, valueList.get(i));
}
if (tablet.rowSize == tablet.getMaxRowNumber()) {
session.insertTablet(tablet, true);
tablet.reset();
}
if (tablet.rowSize != 0) {
session.insertTablet(tablet);
tablet.reset();
}
}
/**
* description: 根据SQL查询
*/
public static SessionDataSet query(String sql) throws StatementExecutionException, IoTDBConnectionException {
return session.executeQueryStatement(sql);
}
/**
* description: 删除分组 如 root.a1eaKSRpRty
*
* @param groupName:分组名称
* @return
*/
public static void deleteStorageGroup(String groupName) throws StatementExecutionException, IoTDBConnectionException {
session.deleteStorageGroup(groupName);
}
/**
* description: 根据Timeseries删除 如:root.a1eaKSRpRty.CA3013A303A25467.breath (个人理解:为具体的物理量)
*/
public static void deleteTimeseries(String timeseries) throws StatementExecutionException, IoTDBConnectionException {
session.deleteTimeseries(timeseries);
}
/**
* description: 根据Timeseries批量删除
*/
public static void deleteTimeserieList(List<String> timeseriesList) throws StatementExecutionException, IoTDBConnectionException {
session.deleteTimeseries(timeseriesList);
}
/**
* description: 根据分组批量删除
*/
public static void deleteStorageGroupList(List<String> storageGroupList) throws StatementExecutionException, IoTDBConnectionException {
session.deleteStorageGroups(storageGroupList);
}
/**
* description: 根据路径和结束时间删除 结束时间之前的所有数据
*/
public static void deleteDataByPathAndEndTime(String path, Long endTime) throws StatementExecutionException, IoTDBConnectionException {
session.deleteData(path, endTime);
}
/**
* description: 根据路径集合和结束时间批量删除 结束时间之前的所有数据
*/
public static void deleteDataByPathListAndEndTime(List<String> pathList, Long endTime) throws StatementExecutionException, IoTDBConnectionException {
session.deleteData(pathList, endTime);
}
/**
* description: 根据路径集合和时间段批量删除
*/
public static void deleteDataByPathListAndTime(List<String> pathList, Long startTime, Long endTime)
throws StatementExecutionException, IoTDBConnectionException {
session.deleteData(pathList, startTime, endTime);
}
/**
* 执行语句无返回值
* @param sql
* @throws IoTDBConnectionException
* @throws StatementExecutionException
*/
public static void exesql(String sql) throws IoTDBConnectionException, StatementExecutionException {
session.executeNonQueryStatement(sql);
}
/**
* SessionDataSet 转 List<Record>
* @param deviceid
* @param sessionDataSet
* @return
* @throws IoTDBConnectionException
* @throws StatementExecutionException
*/
public static List<Record> ToListRecord(String deviceid,SessionDataSet sessionDataSet) throws IoTDBConnectionException, StatementExecutionException {
List<Record> list=new ArrayList<Record>();
List<String> listcols = sessionDataSet.getColumnNames(); //字段信息
sessionDataSet.setFetchSize(1); // 只一个一个的获取
int fetchSize = sessionDataSet.getFetchSize();
int nextNum = 0;
while(sessionDataSet.hasNext()) {
RowRecord next = sessionDataSet.next(); // 如果获取的数据量
List<Field> listRes = next.getFields(); //数据信息
Record record=new Record();
record.set("time",next.getTimestamp());
for (int i = 0; i < listRes.size(); i++) {
String colname=listcols.get(i+1);
colname = colname.substring(colname.indexOf(deviceid.replaceAll("-","_"))+deviceid.length()+1);
Field field = listRes.get(i);
if(field.getDataType()!=null)
{
if(field.getDataType()==TSDataType.DOUBLE)
{
record.set(colname,field.getDoubleV());
}
else if(field.getDataType()==TSDataType.FLOAT)
{
record.set(colname,field.getFloatV());
}
else if(field.getDataType()==TSDataType.INT32||field.getDataType()==TSDataType.INT64)
{
record.set(colname,field.getIntV());
}
else if(field.getDataType()==TSDataType.BOOLEAN)
{
record.set(colname,field.getBoolV());
}
else if(field.getDataType()==TSDataType.TEXT)
{
record.set(colname,field.getStringValue());
}
else
{
record.set(colname,field.getStringValue());
}
}
else
{
record.set(colname,null);
}
}
list.add(record);
nextNum++;
}
sessionDataSet.closeOperationHandle();
return list;
}
/**
* 查询出单参数的时间折线图数据集
* @param deviceid
* @param sessionDataSet
* @return
* @throws IoTDBConnectionException
* @throws StatementExecutionException
*/
public static List<Object> ToList(String deviceid,SessionDataSet sessionDataSet) throws IoTDBConnectionException, StatementExecutionException {
List<Object> data;
List<Object> list_data = new ArrayList<Object>();
List<String> listcols = sessionDataSet.getColumnNames(); //字段信息
sessionDataSet.setFetchSize(1); // 只一个一个的获取
int fetchSize = sessionDataSet.getFetchSize();
int nextNum = 0;
while(sessionDataSet.hasNext()) {
RowRecord next = sessionDataSet.next(); // 如果获取的数据量
List<Field> listRes = next.getFields(); //数据信息
data=new ArrayList<Object>();
data.add(next.getTimestamp()); //时间
for (int i = 0; i < listRes.size(); i++) {
String colname=listcols.get(i+1);
colname = colname.substring(colname.indexOf(deviceid.replaceAll("-","_"))+deviceid.length()+1);
Field field = listRes.get(i);
if(field.getDataType()!=null)
{
if(field.getDataType()==TSDataType.DOUBLE)
{
data.add(field.getDoubleV());
}
else if(field.getDataType()==TSDataType.FLOAT)
{
data.add(field.getFloatV());
}
else if(field.getDataType()==TSDataType.INT32||field.getDataType()==TSDataType.INT64)
{
data.add(field.getIntV());
}
}
else
{
data.add(null);
}
}
list_data.add(data);
nextNum++;
}
sessionDataSet.closeOperationHandle();
return list_data;
}
}
4.JFinal插件启动
jfinal的继承“JFinalConfig”的类文件的“configPlugin(Plugins plugins)”添加插件,启动之后即可连接到IoTDB,我使用的是默认连接,IoTDB只使用地址和端口:
IoTDBPlugin ioTDBPlugin=new IoTDBPlugin(p.get("iotdb.host"),p.getInt("iotdb.port"));
plugins.add(ioTDBPlugin);
5.IoTDB的数据插入
List<String> measurementsList = new ArrayList<>();
List<Double> valuesList = new ArrayList<>();
String deviceId ="root.demo.group.deviceId";
measurementsList.add("temperature");
valuesList.add(30.3);
try {
IoTDBUtils.insertRecord(deviceId,new Date().getTime(), measurementsList, valuesList);
} catch (StatementExecutionException e) {
e.printStackTrace();
} catch (IoTDBConnectionException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
}
6.IoTDB的数据查询
String sql="select temperature from root.demo.group.deviceid where time>=2020-10-10 12:00:00 and time <=2020-10-11 12:00:00";
SessionDataSet sessionDataSet = null;
try {
sessionDataSet = IoTDBUtils.query(sql);
return sessionDataSet;
} catch (StatementExecutionException e) {
e.printStackTrace();
} catch (IoTDBConnectionException e) {
e.printStackTrace();
}
解析“SessionDataSet”即可,可以参考IoTDBUtils方法中的ToList,生成类似如下的折线图。(我做的是多轴的)
总结
IoTDB我自测程序不断写入,上百万的参数数据,查询无压力。解决了当前的物联网项目中的大量数据的存储和查询问题。