diff --git a/.gitignore b/.gitignore index 524f096..30b7352 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,6 @@ -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -replay_pid* +/target/ +.classpath +.project +.settings +.idea +*.iml diff --git a/LICENSE b/LICENSE index 261eeb9..4da1e40 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright ©2021 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md new file mode 100755 index 0000000..6646927 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# apijson-iotdb [![](https://jitpack.io/v/APIJSON/apijson-iotdb.svg)](https://jitpack.io/#APIJSON/apijson-iotdb) +腾讯 [APIJSON](https://github.com/Tencent/APIJSON) 7.0.3+ 的 IoTDB 数据库插件,可通过 Maven, Gradle 等远程依赖。
+An IoTDB plugin for Tencent [APIJSON](https://github.com/Tencent/APIJSON) 7.0.3+ + +![image](https://private-user-images.githubusercontent.com/5738175/397984593-6d088d9c-86d9-40a9-b9fa-b49c679e19a6.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzQ4NzQ5NzQsIm5iZiI6MTczNDg3NDY3NCwicGF0aCI6Ii81NzM4MTc1LzM5Nzk4NDU5My02ZDA4OGQ5Yy04NmQ5LTQwYTktYjlmYS1iNDljNjc5ZTE5YTYucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MTIyMiUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDEyMjJUMTMzNzU0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9OWM2OTBmYmM4MmRiYzIwNjg4ZDAzZDkxYjkwMjE4ZDM4NWY1NDFkYTFjNTMyMWQ0MzFlMzkxODhlNDIxNDYyMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.POonQKkSUjGuF1fRF4vbT61mI1wKGmamLTL5Ld7GmxA) +![image](https://github.com/user-attachments/assets/920dd1ea-5490-4c1e-8132-7e1f6324f156) + +## 添加依赖 +## Add Dependency + +### Maven +#### 1. 在 pom.xml 中添加 JitPack 仓库 +#### 1. Add the JitPack repository to pom.xml +```xml + + + jitpack.io + https://jitpack.io + + +``` + +![image](https://user-images.githubusercontent.com/5738175/167261814-d75d8fff-0e64-4534-a840-60ef628a8873.png) + +
+ +#### 2. 在 pom.xml 中添加 apijson-iotdb 依赖 +#### 2. Add the apijson-iotdb dependency to pom.xml +```xml + + com.github.APIJSON + apijson-iotdb + LATEST + +``` + +
+ +https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/APIJSONBoot-MultiDataSource/pom.xml + +
+
+ +### Gradle +#### 1. 在项目根目录 build.gradle 中最后添加 JitPack 仓库 +#### 1. Add the JitPack repository in your root build.gradle at the end of repositories +```gradle + allprojects { + repositories { + maven { url 'https://jitpack.io' } + } + } +``` +
+ +#### 2. 在项目某个 module 目录(例如 `app`) build.gradle 中添加 apijson-iotdb 依赖 +#### 2. Add the apijson-iotdb dependency in one of your modules(such as `app`) +```gradle + dependencies { + implementation 'com.github.APIJSON:apijson-iotdb:latest' + } +``` + +
+
+
+ +## 使用 +## Usage + +在你项目继承 AbstractSQLExecutor 的子类重写方法 execute
+Override execute in your SQLExecutor extends AbstractSQLExecutor + +```java + @Override + public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) throws Exception { + if (config.isIoTDB()) { + return InfluxdbUtil.execute(config, null, unknownType); + } + + return super.execute(config, unknownType); + } +``` + +
+在你项目继承 AbstractSQLConfig 的子类重写方法 execute
+Override execute in your SQLConfig extends AbstractSQLConfig + +```java + @Override + public String getTablePath() { + return IoTDBUtil.getTablePath(super.getTablePath(), isIoTDB()); + } +``` + +#### 见 [IoTDBUtil](/src/main/java/apijson/iotdb/IoTDBUtil.java) 的注释及 [APIJSONBoot-MultiDataSource](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/APIJSONBoot-MultiDataSource) 的 [DemoSQLExecutor](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/demo/DemoSQLExecutor.java)
+ +#### See document in [IoTDBUtil](/src/main/java/apijson/iotdb/IoTDBUtil.java) and [DemoSQLExecutor](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/demo/DemoSQLExecutor.java) in [APIJSONBoot-MultiDataSource](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/APIJSONBoot-MultiDataSource) + +
+
+
+ +有问题可以去 Tencent/APIJSON 提 issue
+https://github.com/Tencent/APIJSON/issues/36 + +

+ +#### 点右上角 ⭐Star 支持一下,谢谢 ^_^ +#### Please ⭐Star this project ^_^ +https://github.com/APIJSON/apijson-iotdb diff --git a/pom.xml b/pom.xml new file mode 100755 index 0000000..3f114a8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + apijson.iotdb + apijson-iotdb + 1.0.0 + jar + + apijson-iotdb + A IoTDB plugin for Tencent APIJSON + + + UTF-8 + UTF-8 + 1.8 + + + + + + com.alibaba + fastjson + 1.2.83 + + + com.github.Tencent + APIJSON + 7.0.3 + + + org.apache.iotdb + iotdb-session + 1.3.1 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + + + + + + jitpack.io + https://jitpack.io + + true + + + + + diff --git a/src/main/java/apijson/iotdb/IoTDBUtil.java b/src/main/java/apijson/iotdb/IoTDBUtil.java new file mode 100644 index 0000000..7f2fb52 --- /dev/null +++ b/src/main/java/apijson/iotdb/IoTDBUtil.java @@ -0,0 +1,299 @@ +/*Copyright ©2024 APIJSON(https://github.com/APIJSON) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.*/ + +package apijson.iotdb; + +import apijson.JSONResponse; +import apijson.NotNull; +import apijson.RequestMethod; +import apijson.StringUtil; +import apijson.orm.AbstractParser; +import apijson.orm.SQLConfig; +import com.alibaba.fastjson.JSONObject; +import org.apache.iotdb.isession.SessionDataSet; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.session.Session; +import org.apache.iotdb.tsfile.read.common.Field; +import org.apache.iotdb.tsfile.read.common.RowRecord; + +import java.util.*; + +import static apijson.orm.AbstractSQLExecutor.KEY_RAW_LIST; + + +/** + * @author Lemon + * @see DemoSQLExecutor 重写 execute 方法: + * \@Override + * public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) throws Exception { + * if (config.isIoTDB()) { + * return IoTDBUtil.execute(config, null, unknownType); + * } + * + * return super.execute(config, unknownType); + * } + * + * DemoSQLConfig 重写方法 getSchema, getSQLSchema 方法 + * \@Override + * public String getSchema() { + * return IoTDBUtil.getSchema(super.getSchema(), DEFAULT_SCHEMA, isIoTDB()); + * } + * + * \@Override + * public String getSQLSchema() { + * return IoTDBUtil.getSQLSchema(super.getSQLSchema(), isIoTDB()); + * } + */ +public class IoTDBUtil { + public static final String TAG = "IoTDBUtil"; + + public static String getSchema(String schema, String defaultSchema) { + return getSchema(schema, defaultSchema, true); + } + public static String getSchema(String schema, String defaultSchema, boolean isIoTDB) { + if (StringUtil.isEmpty(schema) && isIoTDB) { + schema = defaultSchema; + } + return schema; + } + + public static String getSQLSchema(String schema) { + return getSQLSchema(schema, true); + } + public static String getSQLSchema(String schema, boolean isIoTDB) { + return schema; + } + + public static String getTablePath(String path) { + return getTablePath(path, true); + } + public static String getTablePath(String path, boolean isIoTDB) { + if (isIoTDB) { + String[] ks = path == null || path.trim().isEmpty() ? null : path.trim().split("\\."); + int len = ks == null ? 0 : ks.length; + return len <= 0 ? "**" : (len >= 3 ? path : path + ".**"); + } + + return path; + } + + public static String getSessionKey(@NotNull SQLConfig config) { + String uri = config.getDBUri(); + return uri + (uri.contains("?") ? "&" : "?") + "username=" + config.getDBAccount(); + } + + public static final Map CLIENT_MAP = new LinkedHashMap<>(); + public static Session getSession(@NotNull SQLConfig config) throws IoTDBConnectionException { + return getSession(config, true); + } + public static Session getSession(@NotNull SQLConfig config, boolean autoNew) throws IoTDBConnectionException { + String key = getSessionKey(config); + + Session session = CLIENT_MAP.get(key); + if (autoNew && session == null) { + String uri = config.getDBUri(); + int ind = uri.indexOf("://"); + String host = ind < 0 ? uri : uri.substring(ind + 3); + int ind2 = host.indexOf("?"); + host = ind2 < 0 ? host : host.substring(0, ind2); + int ind3 = host.indexOf(":"); + String portStr = ind < 0 ? null : host.substring(ind3 + 1); + host = ind3 < 0 ? host : host.substring(0, ind3); + int port = portStr == null || portStr.trim().isEmpty() ? 6667 : Integer.valueOf(portStr); + + session = new Session.Builder() + .host(host) + .port(port) + .username(config.getDBAccount()) + .password(config.getDBPassword()) + .build(); + session.open(); + + Session finalSession = session; + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + finalSession.close(); + } catch (IoTDBConnectionException e) { + e.printStackTrace(); // throw new RuntimeException(e); + } + })); + + CLIENT_MAP.put(key, session); + } + + return session; + } + + public static void closeSession(@NotNull SQLConfig config) { + try { + Session session = getSession(config, false); + if (session != null) { + String key = getSessionKey(config); + CLIENT_MAP.remove(key); + + try { + session.close(); + } + catch (Throwable e) { + e.printStackTrace(); + } + } + } + catch (Throwable e) { + e.printStackTrace(); + } + } + + public static void closeAllSession() { + Collection cs = CLIENT_MAP.values(); + for (Session c : cs) { + try { + c.close(); + } + catch (Throwable e) { + e.printStackTrace(); + } + } + + CLIENT_MAP.clear(); + } + + + public static JSONObject execute(@NotNull SQLConfig config, String sql, boolean unknownType) throws Exception { + if (RequestMethod.isQueryMethod(config.getMethod())) { + List list = executeQuery(config, sql, unknownType); + JSONObject result = list == null || list.isEmpty() ? null : list.get(0); + if (result == null) { + result = new JSONObject(true); + } + + if (list != null && list.size() > 1) { + result.put(KEY_RAW_LIST, list); + } + + return result; + } + + return executeUpdate(config, sql); + } + + public static int execUpdate(SQLConfig config, String sql) throws Exception { + JSONObject result = executeUpdate(config, sql); + return result.getIntValue(JSONResponse.KEY_COUNT); + } + + public static JSONObject executeUpdate(SQLConfig config, String sql) throws Exception { + return executeUpdate(null, config, sql); + } + public static JSONObject executeUpdate(Session session, SQLConfig config, String sql) throws Exception { + if (session == null) { + session = getSession(config); + } + + session.executeNonQueryStatement(sql); + + JSONObject result = AbstractParser.newSuccessResult(); + + RequestMethod method = config.getMethod(); + if (method == RequestMethod.POST) { + List> values = config.getValues(); + result.put(JSONResponse.KEY_COUNT, values == null ? 0 : values.size()); + } else { + String idKey = config.getIdKey(); + Object id = config.getId(); + Object idIn = config.getIdIn(); + if (id != null) { + result.put(idKey, id); + } + if (idIn != null) { + result.put(idKey + "[]", idIn); + } + + if (method == RequestMethod.PUT) { + Map content = config.getContent(); + result.put(JSONResponse.KEY_COUNT, content == null ? 0 : content.size()); + } else { + result.put(JSONResponse.KEY_COUNT, id == null && idIn instanceof Collection ? ((Collection) idIn).size() : 1); // FIXME 直接 SQLAuto 传 Flux/InfluxQL INSERT 如何取数量? + } + } + + return result; + } + + + public static JSONObject execQuery(@NotNull SQLConfig config, String sql, boolean unknownType) throws Exception { + List list = executeQuery(config, sql, unknownType); + JSONObject result = list == null || list.isEmpty() ? null : list.get(0); + if (result == null) { + result = new JSONObject(true); + } + + if (list != null && list.size() > 1) { + result.put(KEY_RAW_LIST, list); + } + + return result; + } + + public static List executeQuery(@NotNull SQLConfig config, String sql, boolean unknownType) throws Exception { + return executeQuery(null, config, sql, unknownType); + } + public static List executeQuery(Session session, @NotNull SQLConfig config, String sql, boolean unknownType) throws Exception { + if (session == null) { + session = getSession(config); + } + +// session.setDatabase(config.getSchema()); + + SessionDataSet ds = session.executeQueryStatement(sql); + List ns = ds == null ? null : ds.getColumnNames(); + List nameList = ns == null || ns.isEmpty() ? null : new ArrayList<>(ns.size()); + + if (nameList != null) { + String prefix = config.getSQLSchema() + "." + config.getSQLTable() + "."; + + for (String name : ns) { + if (name.startsWith(prefix)) { + name = name.substring(prefix.length()); + } + + nameList.add(name); + } + } + + if (nameList == null || nameList.isEmpty()) { + return null; + } + + List resultList = new ArrayList<>(ds.getFetchSize()); + + while (ds.hasNext()) { + RowRecord row = ds.next(); + List fs = row.getFields(); + + JSONObject obj = new JSONObject(true); + obj.put(nameList.get(0), row.getTimestamp()); + for (int i = 0; i < fs.size(); i++) { + Field f = fs.get(i); + Object v = f == null ? null : f.getObjectValue(f.getDataType()); + obj.put(nameList.get(i + 1), v); + } + + resultList.add(obj); + } + + return resultList; + } + +}