From 9b9086af8f3f5a9fc0288c015c357abbce4c617b Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 1 Mar 2025 16:28:22 +0800
Subject: [PATCH 01/91] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20Co?=
=?UTF-8?q?ckroachDB-=E4=BA=91=E5=8E=9F=E7=94=9F=E5=88=86=E5=B8=83?=
=?UTF-8?q?=E5=BC=8F=E9=AB=98=E5=8F=AF=E7=94=A8=E8=BD=BB=E6=9D=BE=E6=89=A9?=
=?UTF-8?q?=E5=B1=95=E6=95=B0=E6=8D=AE=E5=BA=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../java/apijson/orm/AbstractSQLConfig.java | 29 +++++---
.../java/apijson/orm/AbstractSQLExecutor.java | 2 +-
.../src/main/java/apijson/orm/SQLConfig.java | 16 +++--
.../orm/exception/CommonException.java | 71 +++++++++++++++++--
6 files changed, 100 insertions(+), 22 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 475e6ba91..f3c7bb589 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.5.6
+ 7.6.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 6183afea8..12fe35cc4 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.5.5";
+ public static final String VERSION = "7.6.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index aed2efb62..53d8633ab 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -95,8 +95,8 @@ public abstract class AbstractSQLConfig implements SQLConfig implements SQLConfig setTable(String table) { //Table已经在Parser中校验,所以这里不用防SQL注入
@@ -3955,7 +3968,7 @@ public String getRegExpString(String key, String column, Object[] values, int ty
*/
@JSONField(serialize = false)
public String getRegExpString(String key, String column, String value, boolean ignoreCase) {
- if (isPostgreSQL() || isInfluxDB()) {
+ if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
return getKey(column) + " ~" + (ignoreCase ? "* " : " ") + getValue(key, column, value);
}
if (isOracle() || isDameng() || isKingBase() || (isMySQL() && getDBVersionNums()[0] >= 8)) {
@@ -4281,7 +4294,7 @@ public String getContainString(String key, String column, Object[] childs, int t
}
condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR));
- if (isPostgreSQL() || isInfluxDB()) {
+ if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
condition += (getKey(column) + " @> " + getValue(key, column, newJSONArray(c)));
// operator does not exist: jsonb @> character varying "[" + c + "]");
}
@@ -4987,7 +5000,7 @@ else if (l > 0 && StringUtil.isName(String.valueOf(l))) {
}
else if (rt.endsWith("~")) {
boolean ignoreCase = "*~".equals(rt);
- if (isPostgreSQL() || isInfluxDB()) {
+ if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
sql += (first ? ON : AND) + lk + (isNot ? NOT : "") + " ~" + (ignoreCase ? "* " : " ") + rk;
}
else if (isOracle() || isDameng() || isKingBase()) {
@@ -5042,7 +5055,7 @@ else if ("{}".equals(rt) || "<>".equals(rt)) {
String arrKeyPath = isIn ? rk : lk;
String itemKeyPath = isIn ? lk : rk;
- if (isPostgreSQL() || isInfluxDB()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
+ if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
sql += (first ? ON : AND) + (isNot ? "( " : "") + getCondition(isNot, arrKeyPath
+ " IS NOT NULL AND " + arrKeyPath + " @> " + itemKeyPath) + (isNot ? ") " : "");
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 9cddeb4e1..9393f43be 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1190,7 +1190,7 @@ else if (RequestMethod.isGetMethod(config.getMethod(), true)) {
//} else {
// statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
//}
- if (config.isMySQL() || config.isPostgreSQL() || config.isOracle() || config.isSQLServer() || config.isDb2()) {
+ if (config.isMySQL() || config.isPostgreSQL() || config.isCockroachDB() || config.isOracle() || config.isSQLServer() || config.isDb2()) {
statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
} else {
statement = getConnection(config).prepareStatement(sql);
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 6252bc68a..d70de4b0e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -22,12 +22,13 @@ public interface SQLConfig {
String DATABASE_SQLSERVER = "SQLSERVER"; // https://www.microsoft.com/en-us/sql-server
String DATABASE_ORACLE = "ORACLE"; // https://www.oracle.com/database
String DATABASE_DB2 = "DB2"; // https://www.ibm.com/products/db2
- String DATABASE_MARIADB = "MARIADB"; // https://mariadb.org
- String DATABASE_TIDB = "TIDB"; // https://www.pingcap.com/tidb
- String DATABASE_DAMENG = "DAMENG"; // https://www.dameng.com
- String DATABASE_KINGBASE = "KINGBASE"; // https://www.kingbase.com.cn
- String DATABASE_ELASTICSEARCH = "ELASTICSEARCH"; // https://www.elastic.co/guide/en/elasticsearch/reference/7.4/xpack-sql.html
- String DATABASE_CLICKHOUSE = "CLICKHOUSE"; // https://clickhouse.com
+ String DATABASE_MARIADB = "MARIADB"; // https://mariadb.org
+ String DATABASE_TIDB = "TIDB"; // https://www.pingcap.com/tidb
+ String DATABASE_COCKROACHDB = "COCKROACHDB"; // https://www.cockroachlabs.com
+ String DATABASE_DAMENG = "DAMENG"; // https://www.dameng.com
+ String DATABASE_KINGBASE = "KINGBASE"; // https://www.kingbase.com.cn
+ String DATABASE_ELASTICSEARCH = "ELASTICSEARCH"; // https://www.elastic.co/guide/en/elasticsearch/reference/7.4/xpack-sql.html
+ String DATABASE_CLICKHOUSE = "CLICKHOUSE"; // https://clickhouse.com
String DATABASE_HIVE = "HIVE"; // https://hive.apache.org
String DATABASE_PRESTO = "PRESTO"; // Facebook PrestoDB https://prestodb.io
String DATABASE_TRINO = "TRINO"; // PrestoSQL https://trino.io
@@ -45,7 +46,7 @@ public interface SQLConfig {
String DATABASE_SQLITE = "SQLITE"; // https://www.sqlite.org
String DATABASE_DUCKDB = "DUCKDB"; // https://duckdb.org
String DATABASE_SURREALDB = "SURREALDB"; // https://surrealdb.com
- String DATABASE_OPENGAUSS = "OPENGAUSS"; // https://surrealdb.com
+ String DATABASE_OPENGAUSS = "OPENGAUSS"; // https://opengauss.org
String DATABASE_MQ = "MQ"; //
@@ -81,6 +82,7 @@ public interface SQLConfig {
boolean isDb2();
boolean isMariaDB();
boolean isTiDB();
+ boolean isCockroachDB();
boolean isDameng();
boolean isKingBase();
boolean isElasticsearch();
diff --git a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
index fe748b7a1..6dc15129c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
+++ b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
@@ -147,7 +147,7 @@ public CommonException(Throwable t, String environment) {
}
- public static Exception wrap(Exception e, SQLConfig config) {
+ public static Exception wrap(Exception e, SQLConfig> config) {
if (Log.DEBUG == false && e instanceof SQLException) {
return new SQLException("数据库驱动执行异常SQLException,非 Log.DEBUG 模式下不显示详情,避免泄漏真实模式名、表名等隐私信息", e);
}
@@ -158,23 +158,32 @@ public static Exception wrap(Exception e, SQLConfig config) {
// msg != null && msg.contains(Log.KEY_SYSTEM_INFO_DIVIDER) == false) {
try {
String db = config == null ? AbstractSQLConfig.DEFAULT_DATABASE : (config instanceof AbstractSQLConfig
- ? ((AbstractSQLConfig) config).getSQLDatabase() : config.getDatabase()
+ ? ((AbstractSQLConfig>) config).getSQLDatabase() : config.getDatabase()
);
- String dbVersion = config.getDBVersion();
+ String dbVersion = config == null ? null : config.getDBVersion();
if (StringUtil.isEmpty(dbVersion)) {
dbVersion = "";
}
- if (db != null) {
+ if (db != null || config == null) {
db += " " + dbVersion;
}
else if (config.isMySQL()) {
db = SQLConfig.DATABASE_MYSQL + " " + dbVersion;
}
+ else if (config.isMariaDB()) {
+ db = SQLConfig.DATABASE_MARIADB + " " + dbVersion;
+ }
+ else if (config.isTiDB()) {
+ db = SQLConfig.DATABASE_TIDB + " " + dbVersion;
+ }
else if (config.isPostgreSQL()) {
db = SQLConfig.DATABASE_POSTGRESQL + " " + dbVersion;
}
+ else if (config.isCockroachDB()) {
+ db = SQLConfig.DATABASE_COCKROACHDB + " " + dbVersion;
+ }
else if (config.isSQLServer()) {
db = SQLConfig.DATABASE_SQLSERVER + " " + dbVersion;
}
@@ -184,15 +193,69 @@ else if (config.isOracle()) {
else if (config.isDb2()) {
db = SQLConfig.DATABASE_DB2 + " " + dbVersion;
}
+ else if (config.isDuckDB()) {
+ db = SQLConfig.DATABASE_DUCKDB + " " + dbVersion;
+ }
+ else if (config.isSurrealDB()) {
+ db = SQLConfig.DATABASE_SURREALDB + " " + dbVersion;
+ }
+ else if (config.isOpenGauss()) {
+ db = SQLConfig.DATABASE_OPENGAUSS + " " + dbVersion;
+ }
else if (config.isDameng()) {
db = SQLConfig.DATABASE_DAMENG + " " + dbVersion;
}
+ else if (config.isKingBase()) {
+ db = SQLConfig.DATABASE_KINGBASE + " " + dbVersion;
+ }
+ else if (config.isElasticsearch()) {
+ db = SQLConfig.DATABASE_ELASTICSEARCH + " " + dbVersion;
+ }
else if (config.isClickHouse()) {
db = SQLConfig.DATABASE_CLICKHOUSE + " " + dbVersion;
}
+ else if (config.isMilvus()) {
+ db = SQLConfig.DATABASE_MILVUS + " " + dbVersion;
+ }
+ else if (config.isInfluxDB()) {
+ db = SQLConfig.DATABASE_INFLUXDB + " " + dbVersion;
+ }
else if (config.isTDengine()) {
db = SQLConfig.DATABASE_TDENGINE + " " + dbVersion;
}
+ else if (config.isIoTDB()) {
+ db = SQLConfig.DATABASE_IOTDB + " " + dbVersion;
+ }
+ else if (config.isSQLite()) {
+ db = SQLConfig.DATABASE_SQLITE + " " + dbVersion;
+ }
+ else if (config.isHive()) {
+ db = SQLConfig.DATABASE_HIVE + " " + dbVersion;
+ }
+ else if (config.isPresto()) {
+ db = SQLConfig.DATABASE_PRESTO + " " + dbVersion;
+ }
+ else if (config.isTrino()) {
+ db = SQLConfig.DATABASE_TRINO + " " + dbVersion;
+ }
+ else if (config.isSnowflake()) {
+ db = SQLConfig.DATABASE_SNOWFLAKE + " " + dbVersion;
+ }
+ else if (config.isDatabricks()) {
+ db = SQLConfig.DATABASE_DATABRICKS + " " + dbVersion;
+ }
+ else if (config.isMongoDB()) {
+ db = SQLConfig.DATABASE_MONGODB + " " + dbVersion;
+ }
+ else if (config.isCassandra()) {
+ db = SQLConfig.DATABASE_CASSANDRA + " " + dbVersion;
+ }
+ else if (config.isRedis()) {
+ db = SQLConfig.DATABASE_REDIS + " " + dbVersion;
+ }
+ else if (config.isKafka()) {
+ db = SQLConfig.DATABASE_KAFKA + " " + dbVersion;
+ }
else {
db = "";
}
From bc1035fec2356feb6f0cd81a6bd8f2b98d6a63c8 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 1 Mar 2025 17:46:11 +0800
Subject: [PATCH 02/91] =?UTF-8?q?=E4=BC=98=E5=8C=96=20SQLConfig=20?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BC=A9=E8=BF=9B=EF=BC=8C=E8=A7=A3=E5=86=B3?=
=?UTF-8?q?=E5=9C=A8=20GitHub=20=E4=B8=8A=E5=9B=A0=E4=B8=BA=20tab=20?=
=?UTF-8?q?=E7=BC=A9=E8=BF=9B=E5=92=8C=20IDEA=20=E4=B8=8D=E4=B8=80?=
=?UTF-8?q?=E8=87=B4=E5=AF=BC=E8=87=B4=E4=B8=8A=E4=B8=8B=E6=98=BE=E7=A4=BA?=
=?UTF-8?q?=E6=B2=A1=E5=AF=B9=E9=BD=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/SQLConfig.java | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index d70de4b0e..e63687976 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -113,9 +113,9 @@ public interface SQLConfig {
// boolean isPLSQL();
// boolean isAnsiSQL();
- /**用来给 Table, Column 等系统属性表来绕过 MAX_SQL_COUNT 等限制
- * @return
- */
+ /**用来给 Table, Column 等系统属性表来绕过 MAX_SQL_COUNT 等限制
+ * @return
+ */
boolean limitSQLCount();
/**是否开启 WITH AS 表达式来简化 SQL 和提升性能
@@ -123,11 +123,11 @@ public interface SQLConfig {
*/
boolean isWithAsEnable();
/**允许增删改部分失败
- * @return
- */
- boolean allowPartialUpdateFailed();
+ * @return
+ */
+ boolean allowPartialUpdateFailed();
- @NotNull
+ @NotNull
String getIdKey();
@NotNull
String getUserIdKey();
From de742e729b38a47bfc35b5e811ce53a33b9e7fa0 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 2 Mar 2025 12:00:24 +0800
Subject: [PATCH 03/91] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20Ma?=
=?UTF-8?q?nticoreSearch-=E6=9B=BF=E4=BB=A3=20Elasticsearch=20=E7=9A=84?=
=?UTF-8?q?=E8=BD=BB=E9=87=8F=E7=BA=A7=E6=90=9C=E7=B4=A2=E5=BC=95=E6=93=8E?=
=?UTF-8?q?=EF=BC=8C=E5=85=BC=E5=AE=B9=20MySQL=20=E5=8D=8F=E8=AE=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../src/main/java/apijson/orm/AbstractSQLConfig.java | 11 ++++++++++-
APIJSONORM/src/main/java/apijson/orm/SQLConfig.java | 2 ++
.../java/apijson/orm/exception/CommonException.java | 3 +++
5 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index f3c7bb589..179373256 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.6.0
+ 7.7.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 12fe35cc4..6f9aa894c 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.6.0";
+ public static final String VERSION = "7.7.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 53d8633ab..9b901e6cb 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -185,6 +185,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
String DATABASE_DAMENG = "DAMENG"; // https://www.dameng.com
String DATABASE_KINGBASE = "KINGBASE"; // https://www.kingbase.com.cn
String DATABASE_ELASTICSEARCH = "ELASTICSEARCH"; // https://www.elastic.co/guide/en/elasticsearch/reference/7.4/xpack-sql.html
+ String DATABASE_MANTICORE = "MANTICORE"; // https://manticoresearch.com
String DATABASE_CLICKHOUSE = "CLICKHOUSE"; // https://clickhouse.com
String DATABASE_HIVE = "HIVE"; // https://hive.apache.org
String DATABASE_PRESTO = "PRESTO"; // Facebook PrestoDB https://prestodb.io
@@ -86,6 +87,7 @@ public interface SQLConfig {
boolean isDameng();
boolean isKingBase();
boolean isElasticsearch();
+ boolean isManticore();
boolean isClickHouse();
boolean isHive();
boolean isPresto();
diff --git a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
index 6dc15129c..23055198c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
+++ b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
@@ -211,6 +211,9 @@ else if (config.isKingBase()) {
else if (config.isElasticsearch()) {
db = SQLConfig.DATABASE_ELASTICSEARCH + " " + dbVersion;
}
+ else if (config.isManticore()) {
+ db = SQLConfig.DATABASE_MANTICORE + " " + dbVersion;
+ }
else if (config.isClickHouse()) {
db = SQLConfig.DATABASE_CLICKHOUSE + " " + dbVersion;
}
From 013441a045b5935bde59f6a54edc73340515f480 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Mon, 3 Mar 2025 00:35:19 +0800
Subject: [PATCH 04/91] =?UTF-8?q?=E5=8E=BB=E6=8E=89=20ManticoreSearch=20?=
=?UTF-8?q?=E4=B8=8D=E6=94=AF=E6=8C=81=E7=9A=84=20SQL=20=E5=85=B3=E9=94=AE?=
=?UTF-8?q?=E8=AF=8D=20AS?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 9b901e6cb..1512a5529 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1494,7 +1494,7 @@ public AbstractSQLConfig setTable(String table) { //Table已经在Parser中
}
public String getAs() {
- return isOracle() ? " " : " AS ";
+ return isOracle() || isManticore() ? " " : " AS ";
}
@Override
From 7ff081260e018f2217ba8f84693d4fad3b06601e Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 3 Mar 2025 00:54:57 +0800
Subject: [PATCH 05/91] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20CockroachDB,=20Manti?=
=?UTF-8?q?coreSearch,=20PosgGIS=20=E7=9A=84=E6=94=AF=E6=8C=81=E8=AF=B4?=
=?UTF-8?q?=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#--apijson
---
README.md | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e373f91b5..1f71bde90 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -30,6 +31,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -43,7 +45,8 @@ This source code is licensed under the Apache License Version 2.0
-
+
+
From 54ecd56fc71412a8cf3e1742cbb0b97e122e5fb7 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 3 Mar 2025 00:57:12 +0800
Subject: [PATCH 06/91] =?UTF-8?q?=20=E6=96=B0=E5=A2=9E=20CockroachDB,=20Ma?=
=?UTF-8?q?nticoreSearch,=20PosgGIS=20=E7=9A=84=E6=94=AF=E6=8C=81=E8=AF=B4?=
=?UTF-8?q?=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#--apijson
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 1f71bde90..721e0adf6 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,7 @@ This source code is licensed under the Apache License Version 2.0
+
From 69573d1d6dfd6101ccfc993b5e4ceb6d1e61de8f Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 3 Mar 2025 00:59:27 +0800
Subject: [PATCH 07/91] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20CockroachDB,=20Manti?=
=?UTF-8?q?coreSearch,=20PosgGIS=20=E7=9A=84=E6=94=AF=E6=8C=81=E8=AF=B4?=
=?UTF-8?q?=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#--apijson
---
README.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 721e0adf6..57bed4104 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -47,8 +48,6 @@ This source code is licensed under the Apache License Version 2.0
-
-
From 86fd75a05ea581afac88d8a0bf20ef9453ce591b Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 9 Mar 2025 22:12:51 +0800
Subject: [PATCH 08/91] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20Ti?=
=?UTF-8?q?mescaleDB-=E9=AB=98=E6=80=A7=E8=83=BD=E5=AE=9E=E6=97=B6?=
=?UTF-8?q?=E5=88=86=E6=9E=90=E6=97=B6=E5=BA=8F=E6=95=B0=E6=8D=AE=E5=BA=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../src/main/java/apijson/orm/AbstractSQLConfig.java | 9 +++++++++
APIJSONORM/src/main/java/apijson/orm/SQLConfig.java | 2 ++
.../main/java/apijson/orm/exception/CommonException.java | 3 +++
5 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 179373256..2d0768fff 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.7.0
+ 7.8.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 6f9aa894c..86c2e61cc 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.7.0";
+ public static final String VERSION = "7.8.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 1512a5529..19446e69e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -193,6 +193,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
String DATABASE_MILVUS = "MILVUS"; // https://milvus.io
String DATABASE_INFLUXDB = "INFLUXDB"; // https://www.influxdata.com/products/influxdb-overview
String DATABASE_TDENGINE = "TDENGINE"; // https://tdengine.com
+ String DATABASE_TIMESCALEDB = "TIMESCALEDB"; // https://www.timescale.com
String DATABASE_IOTDB = "IOTDB"; // https://iotdb.apache.org/zh/UserGuide/latest/API/Programming-JDBC.html
String DATABASE_REDIS = "REDIS"; // https://redisql.com
@@ -98,6 +99,7 @@ public interface SQLConfig {
boolean isMilvus();
boolean isInfluxDB();
boolean isTDengine();
+ boolean isTimescaleDB();
boolean isIoTDB();
boolean isRedis();
boolean isMongoDB();
diff --git a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
index 23055198c..9dab4ea16 100755
--- a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
+++ b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
@@ -226,6 +226,9 @@ else if (config.isInfluxDB()) {
else if (config.isTDengine()) {
db = SQLConfig.DATABASE_TDENGINE + " " + dbVersion;
}
+ else if (config.isTimescaleDB()) {
+ db = SQLConfig.DATABASE_TIMESCALEDB + " " + dbVersion;
+ }
else if (config.isIoTDB()) {
db = SQLConfig.DATABASE_IOTDB + " " + dbVersion;
}
From f2f7fe660980b35b854dc196d0551547121b456b Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 9 Mar 2025 22:36:36 +0800
Subject: [PATCH 09/91] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20TimescaleDB-?=
=?UTF-8?q?=E9=AB=98=E6=80=A7=E8=83=BD=E5=AE=9E=E6=97=B6=E5=88=86=E6=9E=90?=
=?UTF-8?q?=E6=97=B6=E5=BA=8F=E6=95=B0=E6=8D=AE=E5=BA=93=20=E7=9A=84?=
=?UTF-8?q?=E5=BF=AB=E6=8D=B7=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#--apijson
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 57bed4104..a5f474067 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,7 @@ This source code is licensed under the Apache License Version 2.0
+
From ecb9bb729683e390332c45bd056a2a364451370c Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Mar 2025 20:52:55 +0800
Subject: [PATCH 10/91] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E4=BB=A3=E7=A0=81?=
=?UTF-8?q?=E5=AE=89=E5=85=A8=E6=89=AB=E6=8F=8F=E9=85=8D=E7=BD=AE=20yaml?=
=?UTF-8?q?=20=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
github/Tencent/APIJSON.yml | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 github/Tencent/APIJSON.yml
diff --git a/github/Tencent/APIJSON.yml b/github/Tencent/APIJSON.yml
new file mode 100644
index 000000000..e9c40e3e0
--- /dev/null
+++ b/github/Tencent/APIJSON.yml
@@ -0,0 +1,13 @@
+tosr_no:
+
+account_mappings:
+ caohao-go: smallhowcao
+
+opensource_repository_information:
+ tencentopen_url: https://github.com/Tencent/APIJSON
+ tencentopen_name: APIJSON
+ status: ongoing
+ owner: smallhowcao
+ follower: smallhowcao
+ internal_id:
+ internal_url:
From 476f6e2a181fc3f110c92deb9d9f1bef6c6cbf3a Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Mar 2025 21:26:58 +0800
Subject: [PATCH 11/91] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Document-English.md | 28 ++++++++++++++--------------
Document.md | 30 +++++++++++++++---------------
2 files changed, 29 insertions(+), 29 deletions(-)
diff --git a/Document-English.md b/Document-English.md
index 8d1fab8a4..c9a4f2948 100644
--- a/Document-English.md
+++ b/Document-English.md
@@ -4,7 +4,7 @@
Request:
{
"User":{
- }
+ }
}
@@ -44,10 +44,10 @@ Response:
Request:
{
"[]":{
- "count":3, //just get 3 results
- "User":{
- "@column":"id,name" //just get ids and names
- }
+ "count":3, //just get 3 results
+ "User":{
+ "@column":"id,name" //just get ids and names
+ }
}
}
@@ -134,13 +134,13 @@ Response:
Request:
{
"[]":{ //get an array
- "page":0, //pagination
+ "page":0, //pagination
"count":2,
- "Moment":{ //get a Moment
- "content$":"%a%" //filter condition: content contains 'a'
+ "Moment":{ //get a Moment
+ "content$":"%a%" //filter condition: content contains 'a'
},
"User":{
- "id@":"/Moment/userId", //User.id = Moment.userId, short reference path,starts from grandparents path
+ "id@":"/Moment/userId", //User.id = Moment.userId, short reference path,starts from grandparents path
"@column":"id,name,head" //get specified keys with the written order
},
"Comment[]":{ //get a Comment array, and unwrap Comment object
@@ -163,7 +163,7 @@ Response:
"id":15,
"userId":70793,
"date":1486541171000,
- "content":"APIJSON is a JSON Transmission Structure Protocol…",
+ "content":"APIJSON is a JSON Transmission Protocol…",
"praiseUserIdList":[
82055,
82002,
@@ -288,14 +288,14 @@ Response:
### 1. Methods and API endpoints
- Methods | URL | Request | Response
+ Methods | URL | Request | Response
------------ | ------------ | ------------ | ------------
-**GET**: A general way to get data. You can use dev tools to make edits in a web browser. | base_url/get/ | { TableName:{ //Add contiditions here. } } Eg. To get a Moment with `id = 235`: { "Moment":{ "id":235 } } | { TableName:{ ... }, "code":200, "msg":"success" } Eg. { "Moment":{ "id":235, "userId":38710, "content":"APIJSON,let interfaces and documents go to hell !" }, "code":200, "msg":"success" }
+**GET**: A general way to get data. You can use dev tools to make edits in a web browser. | base_url/get/ | { TableName:{ //Add contiditions here. } } Eg. To get a Moment with `id = 235`: { "Moment":{ "id":235 } } | { TableName:{ ... }, "code":200, "msg":"success" } Eg. { "Moment":{ "id":235, "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "code":200, "msg":"success" }
**HEAD**: A general way to get counts. You can use dev tools to make edits in a web browser. | base_url/head/ | { TableName:{ … } } {…} are conditions. Eg. Get the number of Moments posted by the user with `id = 38710`: { "Moment":{ "userId":38710 } } | { TableName:{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" }
**GETS**: Get data with high security and confidentiality. Eg. bank accounts, birth date. | base_url/gets/ | You need to add `"tag":tag` with the same level of `Moment:{}`. Others are the same as **GET**. | Same as **GET**.
**HEADS**: Get counts of confidential data(eg. bank account).| base_url/heads/ | You need to add `"tag":tag` with the same level of `Moment:{}`. Others are the same as **HEAD**. | Same as **HEAD**.
-**POST**: Add new data. | base_url/post/ | { TableName:{ … }, "tag":tag } The id in {...} is generated automatically when table is built and can’t be set by the user. Eg. A user with `id = 38710` posts a new Moment: { "Moment":{ "userId":38710, "content":"APIJSON,let interfaces and documents go to hell !" }, "tag":"Moment" } | { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" }
-**PUT**: Make changes to a specific item. Only change the part sent to server. | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } You can also add multiple id as `id{}`. Eg. Make changes to Moment's content with id= 235: { "Moment":{ "id":235, "content":"APIJSON,let interfaces and documents go to hell !" }, "tag":"Moment" } | Same as **POST**.
+**POST**: Add new data. | base_url/post/ | { TableName:{ … }, "tag":tag } The id in {...} is generated automatically when table is built and can’t be set by the user. Eg. A user with `id = 38710` posts a new Moment: { "Moment":{ "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" } | { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" }
+**PUT**: Make changes to a specific item. Only change the part sent to server. | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } You can also add multiple id as `id{}`. Eg. Make changes to Moment's content with id= 235: { "Moment":{ "id":235, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" } | Same as **POST**.
**DELETE**: Delete data. | base_url/delete/ | { TableName:{ "id":id }, "tag":tag } You can also add multiple id as `id{}`. Or Delete contents with multiple id: { "Comment":{ "id{}":[100,110,120] }, "tag":"Comment[]" } | { TableName:{ "code":200, "msg":"success", "id[]":[100,110,120] "count":3 }, "code":200, "msg":"success" } Eg. { "Comment":{ "code":200, "msg":"success", "id[]":[100,110,120], "count":3 }, "code":200, "msg":"success" }
**Note**:
diff --git a/Document.md b/Document.md
index e20bfbb27..a97f7b823 100644
--- a/Document.md
+++ b/Document.md
@@ -30,7 +30,7 @@ https://github.com/Tencent/APIJSON
{
"User":{
"id":38710
- }
+ }
}
@@ -67,10 +67,10 @@ https://github.com/Tencent/APIJSON
请求:
{
"[]":{
- "count":3, //只要3个
- "User":{
- "@column":"id,name" //只要id,name这两个字段
- }
+ "count":3, //只要3个
+ "User":{
+ "@column":"id,name" //只要id,name这两个字段
+ }
}
}
@@ -156,14 +156,14 @@ https://github.com/Tencent/APIJSON
请求:
{
"[]":{ //请求一个数组
- "page":0, //数组条件
+ "page":0, //数组条件
"count":2,
- "Moment":{ //请求一个名为Moment的对象
- "content$":"%a%" //对象条件,搜索content中包含a的动态
+ "Moment":{ //请求一个名为Moment的对象
+ "content$":"%a%" //对象条件,搜索content中包含a的动态
},
"User":{
- "id@":"/Moment/userId", //User.id = Moment.userId 缺省引用赋值路径,从所处容器的父容器路径开始
- "@column":"id,name,head" //指定返回字段
+ "id@":"/Moment/userId", //User.id = Moment.userId 缺省引用赋值路径,从所处容器的父容器路径开始
+ "@column":"id,name,head" //指定返回字段
},
"Comment[]":{ //请求一个名为Comment的数组,并去除Comment包装
"count":2,
@@ -185,7 +185,7 @@ https://github.com/Tencent/APIJSON
"id":15,
"userId":70793,
"date":1486541171000,
- "content":"APIJSON is a JSON Transmission Structure Protocol…",
+ "content":"APIJSON is a JSON Transmission Protocol…",
"praiseUserIdList":[
82055,
82002,
@@ -378,14 +378,14 @@ https://github.com/Tencent/APIJSON
### 3.1 操作方法
- 方法及说明 | URL | Request | Response
+ 方法及说明 | URL | Request | Response
------------ | ------------ | ------------ | ------------
-GET: 普通获取数据, 可用浏览器调试 | base_url/get/ | { TableName:{ … } } {…}内为限制条件 例如获取一个 id = 235 的 Moment: [{ "Moment":{ "id":235 } }](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fget&type=JSON&json={"Moment"%3A{"id"%3A235}}) 后端校验通过后自动解析为 SQL 并执行: `SELECT * FROM Moment WHERE id=235 LIMIT 1` | { TableName:{ ... }, "code":200, "msg":"success" } 例如 { "Moment":{ "id":235, "userId":38710, "content":"APIJSON,let interfaces and documents go to hell !" }, "code":200, "msg":"success" }
+GET: 普通获取数据, 可用浏览器调试 | base_url/get/ | { TableName:{ … } } {…}内为限制条件 例如获取一个 id = 235 的 Moment: [{ "Moment":{ "id":235 } }](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fget&type=JSON&json={"Moment"%3A{"id"%3A235}}) 后端校验通过后自动解析为 SQL 并执行: `SELECT * FROM Moment WHERE id=235 LIMIT 1` | { TableName:{ ... }, "code":200, "msg":"success" } 例如 { "Moment":{ "id":235, "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "code":200, "msg":"success" }
HEAD: 普通获取数量, 可用浏览器调试 | base_url/head/ | { TableName:{ … } } {…}内为限制条件 例如获取一个 id = 38710 的 User 所发布的 Moment 总数: [{ "Moment":{ "userId":38710 } }](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fhead&type=JSON&json={"Moment"%3A{"userId"%3A38710}}) 后端校验通过后自动解析为 SQL 并执行: `SELECT count(*) FROM Moment WHERE userId=38710 LIMIT 1` | { TableName:{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" } 例如 { "Moment":{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" }
GETS: 安全/私密获取数据, 用于获取钱包等 对安全性要求高的数据 | base_url/gets/ | 最外层加一个 "tag":tag,例如 ["tag":"Privacy"](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={"tag"%3A"Privacy","Privacy"%3A{"id"%3A82001}}),其它同GET | 同GET
HEADS: 安全/私密获取数量, 用于获取银行卡数量等 对安全性要求高的数据总数 | base_url/heads/ | 最外层加一个 "tag":tag,例如 ["tag":"Verify"](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fheads&type=JSON&json={"tag"%3A"Verify","Verify"%3A{"phone"%3A13000082001}}),其它同HEAD | 同HEAD
-POST: 新增数据 | base_url/post/ | 单个: { TableName:{ … }, "tag":tag } {…}中id由后端生成,不能传 例如当前登录用户 38710 发布一个新 Comment: [{ "Comment":{ "momentId":12, "content":"APIJSON,let interfaces and documents go to hell !" }, "tag":"Comment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment":{"momentId":12,"content":"APIJSON,let%20interfaces%20and%20documents%20go%20to%20hell%20!"},"tag":"Comment"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(38710,12,'APIJSON,let interfaces and documents go to hell !')` 批量: { TableName\[]:\[{ … }, { … } … ], "tag":tag } {…}中id由后端生成,不能传 例如当前登录用户 82001 发布 2 个 Comment: [{ "Comment[]":[{ "momentId":12, "content":"APIJSON,let interfaces and documents go to hell !" }, { "momentId":15, "content":"APIJSON is a JSON transmision protocol." }], "tag":"Comment:[]" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment[]":[{"momentId":12,"content":"APIJSON,let%20interfaces%20and%20documents%20go%20to%20hell%20!"},{"momentId":15,"content":"APIJSON%20is%20a%20JSON%20transmision%20protocol."}],"tag":"Comment:[]"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(82001,12,'APIJSON,let interfaces and documents go to hell !');` `INSERT INTO Comment(userId,momentId,content) VALUES(82001,15,'APIJSON is a JSON transmision protocol.');` | 单个: { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" } 批量: { TableName:{ "code":200, "msg":"success", "count":5, "id[]":[1, 2, 3, 4, 5] }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "count":2, "id[]":\[1, 2] }, "code":200, "msg":"success" }
-PUT: 修改数据, 只修改所传的字段 | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } {…} 中 id 或 id{} 至少传一个 例如当前登录用户 82001 修改 id = 235 的 Moment 的 content: [{ "Moment":{ "id":235, "content":"APIJSON,let interfaces and documents go to hell !" }, "tag":"Moment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput&type=JSON&json={"Moment":{"id":235,"content":"APIJSON,let%20interfaces%20and%20documents%20go%20to%20hell%20!"},"tag":"Moment"}) 后端校验通过后自动解析为 SQL 并执行: `UPDATE Moment SET content='APIJSON,let interfaces and documents go to hell !' WHERE id=235 AND userId=82001 LIMIT 1` 批量除了 id{}:\[] 也可类似批量 POST,只是每个 {...} 里面都必须有 id。 "tag":"Comment[]" 对应对象 "Comment":{"id{}":[1,2,3]},表示指定记录全部统一设置; "tag":"Comment:[]" 多了冒号,对应数组 "Comment[]":[{"id":1},{"id":2},{"id":3}],表示每项单独设置 | 同POST
+POST: 新增数据 | base_url/post/ | 单个: { TableName:{ … }, "tag":tag } {…}中id由后端生成,不能传 例如当前登录用户 38710 发布一个新 Comment: [{ "Comment":{ "momentId":12, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Comment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment":{"momentId":12,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},"tag":"Comment"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(38710,12,'APIJSON is the real-time coding-free, powerful and secure ORM')` 批量: { TableName\[]:\[{ … }, { … } … ], "tag":tag } {…}中id由后端生成,不能传 例如当前登录用户 82001 发布 2 个 Comment: [{ "Comment[]":[{ "momentId":12, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, { "momentId":15, "content":"APIJSON is a JSON transmision protocol." }], "tag":"Comment:[]" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment[]":[{"momentId":12,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},{"momentId":15,"content":"APIJSON%20is%20a%20JSON%20transmision%20protocol."}],"tag":"Comment:[]"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(82001,12,'APIJSON is the real-time coding-free, powerful and secure ORM');` `INSERT INTO Comment(userId,momentId,content) VALUES(82001,15,'APIJSON is a JSON transmision protocol.');` | 单个: { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" } 批量: { TableName:{ "code":200, "msg":"success", "count":5, "id[]":[1, 2, 3, 4, 5] }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "count":2, "id[]":\[1, 2] }, "code":200, "msg":"success" }
+PUT: 修改数据, 只修改所传的字段 | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } {…} 中 id 或 id{} 至少传一个 例如当前登录用户 82001 修改 id = 235 的 Moment 的 content: [{ "Moment":{ "id":235, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput&type=JSON&json={"Moment":{"id":235,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},"tag":"Moment"}) 后端校验通过后自动解析为 SQL 并执行: `UPDATE Moment SET content='APIJSON is the real-time coding-free, powerful and secure ORM' WHERE id=235 AND userId=82001 LIMIT 1` 批量除了 id{}:\[] 也可类似批量 POST,只是每个 {...} 里面都必须有 id。 "tag":"Comment[]" 对应对象 "Comment":{"id{}":[1,2,3]},表示指定记录全部统一设置; "tag":"Comment:[]" 多了冒号,对应数组 "Comment[]":[{"id":1},{"id":2},{"id":3}],表示每项单独设置 | 同POST
DELETE: 删除数据 | base_url/delete/ | { TableName:{ "id":id }, "tag":tag } {…} 中 id 或 id{} 至少传一个,一般只传 id 或 id{} 例如当前登录用户 82001 批量删除 id = 100,110,120 的 Comment: [{ "Comment":{ "id{}":[100,110,120] }, "tag":"Comment[]" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fdelete&type=JSON&json={"Comment":{"id{}":[100,110,120]},"tag":"Comment[]"}) 后端校验通过后自动解析为 SQL 并执行: `DELETE FROM Comment WHERE id IN(100,110,120) AND userId=82001 LIMIT 3` | { TableName:{ "code":200, "msg":"success", "id[]":[100,110,120] "count":3 }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "id[]":[100,110,120], "count":3 }, "code":200, "msg":"success" }
以上接口的简单形式: base_url/{method}/{tag} | GET: 普通获取数据 base_url/get/{tag} HEAD: 普通获取数量 base_url/head/{tag} GETS: 安全/私密获取数据 base_url/gets/{tag} HEADS: 安全/私密获取数量 base_url/heads/{tag} POST: 新增数据 base_url/post/{tag} PUT: 修改数据 base_url/put/{tag} DELETE: 删除数据 base_url/delete/{tag} | 例如安全/私密获取一个 id = 82001 的 Privacy: [base_url/gets/Privacy/ {"id":82001}](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets%2FPrivacy&type=JSON&json={"id"%3A82001}) 相当于 [base_url/gets/ {"tag":"Privacy", "Privacy":{"id":82001}}](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={"tag"%3A"Privacy","Privacy"%3A{"id"%3A82001}}) 例如批量修改 id = 114, 124 的 Comment 的 content: [base_url/put/Comemnt[]/ { "id{}":[114,124], "content":"test multi put" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput%2FComment[]&type=JSON&json={"id{}"%3A[114,124],"content"%3A"test%20multi%20put"}) 相当于 [base_url/put/ { "tag":"Comment[]", "Comment":{ "id{}":[114,124], "content":"test multi put" } }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput&type=JSON&json={"tag"%3A"Comment[]","Comment"%3A{"id{}"%3A[114,124],"content"%3A"test%20multi%20put"}}) | 同以上对应的方法
From 41be0a8ddc4fee5f61bb6b0ccfe43341f23046e6 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Mar 2025 21:38:11 +0800
Subject: [PATCH 12/91] Delete APIJSON.yml
---
github/Tencent/APIJSON.yml | 13 -------------
1 file changed, 13 deletions(-)
delete mode 100644 github/Tencent/APIJSON.yml
diff --git a/github/Tencent/APIJSON.yml b/github/Tencent/APIJSON.yml
deleted file mode 100644
index e9c40e3e0..000000000
--- a/github/Tencent/APIJSON.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-tosr_no:
-
-account_mappings:
- caohao-go: smallhowcao
-
-opensource_repository_information:
- tencentopen_url: https://github.com/Tencent/APIJSON
- tencentopen_name: APIJSON
- status: ongoing
- owner: smallhowcao
- follower: smallhowcao
- internal_id:
- internal_url:
From 632c806d630b481a8c33b703b32fa64b652f2c81 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Mar 2025 22:00:26 +0800
Subject: [PATCH 13/91] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README-English.md | 20 ++++++++++----------
README.md | 34 +++++++++++++++++-----------------
2 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/README-English.md b/README-English.md
index 9329faf0e..beea69d6c 100644
--- a/README-English.md
+++ b/README-English.md
@@ -17,8 +17,8 @@ This source code is licensed under the Apache License Version 2.0
-
-
+
+
@@ -42,21 +42,21 @@ This source code is licensed under the Apache License Version 2.0
-
-
-
+
+
+
-
-
+
+
-
-
+
+
@@ -179,7 +179,7 @@ See https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/READ
##
3. Frontend usage
You can skip this step and use [APIAuto](https://github.com/TommyLemon/APIAuto) or download App.
-See [Android](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Android/README-English.md), [iOS](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-iOS/README-English.md) or [JavaScript](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-JavaScript/README-English.md)
+See [Android](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Android/README-English.md), [iOS](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-iOS/README-English.md) or [JavaScript](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-JavaScript/README-English.md)
### Download App
diff --git a/README.md b/README.md
index a5f474067..67610d168 100644
--- a/README.md
+++ b/README.md
@@ -10,14 +10,14 @@ This source code is licensed under the Apache License Version 2.0
English
- 通用文档
+ 通用文档
视频教程
测试用例
-
-
+
+
@@ -54,22 +54,22 @@ This source code is licensed under the Apache License Version 2.0
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
@@ -189,16 +189,16 @@ https://github.com/Tencent/APIJSON/wiki
* **解决十大痛点** (可帮前后端开发大幅提振开发效率、强力杜绝联调扯皮、巧妙规避文档缺陷、非常节省流量带宽)
* **开发提速很大** (CRUD 零代码热更新全自动,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
-* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
-* **社区影响力大** (GitHub 17K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
+* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
+* **社区影响力大** (GitHub 17K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前六、腾讯后端 Star 第一、Trending 日周月榜大满贯 等)
* **多样用户案例** (腾讯内有互娱、音乐、微信、云与智慧,外部有华为、华能、百度、快手、中兴、圆通、传音等)
* **适用场景广泛** (社交聊天、阅读资讯、影音娱乐、办公学习 等各种 App、网站、小程序、公众号 等非金融类项目)
* **周边生态丰富** (Android, iOS, Web 等各种 Demo、继承 JSON 的海量生态、零代码 接口测试 和 单元测试 工具等)
-* **文档视频齐全** (项目介绍、快速上手、安装部署 等后端、前端、客户端的 图文解说、视频教程、代码注释 等)
+* **文档视频齐全** (项目介绍、快速上手、安装部署 等后端、前端、客户端的 图文解说、视频教程、代码注释 等)
* **功能丰富强大** (增删改查、分页排序、分组聚合、各种条件、各种 JOIN、各种子查询、跨库连表 等零代码实现)
* **使用安全简单** (自动增删改查、自动生成文档、自动管理版本、自动控制权限、自动校验参数、自动防 SQL 注入)
-* **灵活定制业务** (在后端编写 远程函数,可以拿到 session、version、当前 JSON 对象 等,然后自定义处理)
+* **灵活定制业务** (在后端编写 远程函数,可以拿到 session、version、当前 JSON 对象 等,然后自定义处理)
* **高质可靠代码** (代码严谨规范,蚂蚁集团源伞 Pinpoint 代码扫描分析报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
@@ -276,7 +276,7 @@ https://github.com/Tencent/APIJSON/issues/36
#### 2.前端上手
可以跳过这个步骤,直接使用 [APIAuto-机器学习HTTP接口工具](https://github.com/TommyLemon/APIAuto) 或 下载客户端App。
-见 [Android](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Android) 或 [iOS](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-iOS) 或 [JavaScript](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-JavaScript)
+见 [Android](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Android) 或 [iOS](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-iOS) 或 [JavaScript](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-JavaScript)
### 下载客户端 App
@@ -546,7 +546,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
### 相关推荐
-[APIJSON, 让接口和文档见鬼去吧!](https://my.oschina.net/tommylemon/blog/805459)
+[APIJSON, 接口和文档的终结者!](https://my.oschina.net/tommylemon/blog/805459)
[腾讯业务百万数据 6s 响应,APIJSON 性能优化背后的故事](https://my.oschina.net/tommylemon/blog/5375645)
From a1caa259157632b981d7c274da7ab6859a53652f Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 00:31:08 +0800
Subject: [PATCH 14/91] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20Qu?=
=?UTF-8?q?estDB=20=E5=B9=B6=E6=94=AF=E6=8C=81=20~=20ASOF=20JOIN=EF=BC=8CS?=
=?UTF-8?q?AMPLE=20BY,=20LATEST=20ON,=20PARTITION=20BY,=20FILL(LINEAR)=20?=
=?UTF-8?q?=E7=AD=89=E5=85=B3=E9=94=AE=E8=AF=8D=E5=8F=8A=E4=B8=8E=E8=AF=AD?=
=?UTF-8?q?=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
.../src/main/java/apijson/JSONObject.java | 68 ++++
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../main/java/apijson/orm/AbstractParser.java | 4 +
.../java/apijson/orm/AbstractSQLConfig.java | 377 ++++++++++++++++--
.../java/apijson/orm/AbstractSQLExecutor.java | 12 +-
.../src/main/java/apijson/orm/Join.java | 5 +-
.../src/main/java/apijson/orm/SQLConfig.java | 18 +
.../orm/exception/CommonException.java | 3 +
9 files changed, 461 insertions(+), 30 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 2d0768fff..2cf436bd5 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.8.0
+ 7.9.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index 6f4359019..c69a03569 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -151,6 +151,10 @@ public JSONObject setUserIdIn(List list) {
public static final String KEY_GROUP = "@group"; //分组方式
public static final String KEY_HAVING = "@having"; //聚合函数条件,一般和@group一起用
public static final String KEY_HAVING_AND = "@having&"; //聚合函数条件,一般和@group一起用
+ public static final String KEY_SAMPLE = "@sample"; //取样方式
+ public static final String KEY_LATEST = "@latest"; //最近方式
+ public static final String KEY_PARTITION = "@partition"; //分区方式
+ public static final String KEY_FILL = "@fill"; //填充方式
public static final String KEY_ORDER = "@order"; //排序方式
public static final String KEY_KEY = "@key"; // key 映射,year:left(date,4);name_tag:(name,tag)
public static final String KEY_RAW = "@raw"; // 自定义原始 SQL 片段
@@ -185,6 +189,10 @@ public JSONObject setUserIdIn(List list) {
TABLE_KEY_LIST.add(KEY_GROUP);
TABLE_KEY_LIST.add(KEY_HAVING);
TABLE_KEY_LIST.add(KEY_HAVING_AND);
+ TABLE_KEY_LIST.add(KEY_SAMPLE);
+ TABLE_KEY_LIST.add(KEY_LATEST);
+ TABLE_KEY_LIST.add(KEY_PARTITION);
+ TABLE_KEY_LIST.add(KEY_FILL);
TABLE_KEY_LIST.add(KEY_ORDER);
TABLE_KEY_LIST.add(KEY_KEY);
TABLE_KEY_LIST.add(KEY_RAW);
@@ -410,6 +418,66 @@ public JSONObject setHaving(String keys, boolean isAnd) {
return puts(isAnd ? KEY_HAVING_AND : KEY_HAVING, keys);
}
+ /**set keys for sample by
+ * @param keys key0, key1, key2 ...
+ * @return {@link #setSample(String)}
+ */
+ public JSONObject setSample(String... keys) {
+ return setSample(StringUtil.getString(keys, true));
+ }
+ /**set keys for sample by
+ * @param keys "key0,key1,key2..."
+ * @return
+ */
+ public JSONObject setSample(String keys) {
+ return puts(KEY_SAMPLE, keys);
+ }
+
+ /**set keys for latest on
+ * @param keys key0, key1, key2 ...
+ * @return {@link #setLatest(String)}
+ */
+ public JSONObject setLatest(String... keys) {
+ return setLatest(StringUtil.getString(keys, true));
+ }
+ /**set keys for latest on
+ * @param keys "key0,key1,key2..."
+ * @return
+ */
+ public JSONObject setLatest(String keys) {
+ return puts(KEY_LATEST, keys);
+ }
+
+ /**set keys for partition by
+ * @param keys key0, key1, key2 ...
+ * @return {@link #setPartition(String)}
+ */
+ public JSONObject setPartition(String... keys) {
+ return setPartition(StringUtil.getString(keys, true));
+ }
+ /**set keys for partition by
+ * @param keys key0, key1, key2 ...
+ * @return
+ */
+ public JSONObject setPartition(String keys) {
+ return puts(KEY_PARTITION, keys);
+ }
+
+ /**set keys for fill(key): fill(null), fill(linear), fill(prev)
+ * @param keys key0, key1, key2 ...
+ * @return {@link #setFill(String)}
+ */
+ public JSONObject setFill(String... keys) {
+ return setFill(StringUtil.getString(keys, true));
+ }
+ /**set keys for fill(key): fill(null), fill(linear), fill(prev)
+ * @param keys key0, key1, key2 ...
+ * @return
+ */
+ public JSONObject setFill(String keys) {
+ return puts(KEY_FILL, keys);
+ }
+
/**set keys for order by
* @param keys key0, key1+, key2- ...
* @return {@link #setOrder(String)}
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 86c2e61cc..0916af410 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.8.0";
+ public static final String VERSION = "7.9.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 624830374..fd56b71b9 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1501,6 +1501,10 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_GROUP);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_HAVING);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_HAVING_AND);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_SAMPLE);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_LATEST);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_PARTITION);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_FILL);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_ORDER);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_KEY);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_RAW);
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 19446e69e..e9b6c0a02 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -194,6 +194,7 @@ public abstract class AbstractSQLConfig implements SQLConfig having; //聚合函数的字符串数组,','分隔
+ private String sample; //取样方式的字符串数组,','分隔
+ private String latest; //最近方式的字符串数组,','分隔
+ private String partition; //分区方式的字符串数组,','分隔
+ private String fill; //填充方式的字符串数组,','分隔
private String order; //排序方式的字符串数组,','分隔
+
private Map keyMap; //字段名映射,支持 name_tag:(name,tag) 多字段 IN,year:left(date,4) 截取日期年份等
private List raw; //需要保留原始 SQL 的字段,','分隔
private List json; //需要转为 JSON 的字段,','分隔
@@ -1103,6 +1109,19 @@ public String getSQLDatabase() {
return db == null ? DEFAULT_DATABASE : db; // "" 表示已设置,不需要用全局默认的 StringUtil.isEmpty(db, false)) {
}
+ @Override
+ public boolean isTSQL() { // 兼容 TSQL 语法
+ return isOracle() || isSQLServer() || isDb2();
+ }
+ @Override
+ public boolean isMSQL() { // 兼容 MySQL 语法,但不一定可以使用它的 JDBC/ODBC
+ return isMySQL() || isTiDB() || isMariaDB() || isSQLite() || isTDengine();
+ }
+ @Override
+ public boolean isPSQL() { // 兼容 PostgreSQL 语法,但不一定可以使用它的 JDBC/ODBC
+ return isPostgreSQL() || isCockroachDB() || isOpenGauss() || isInfluxDB() || isTimescaleDB() || isQuestDB() || isDuckDB();
+ }
+
@Override
public boolean isMySQL() {
return isMySQL(getSQLDatabase());
@@ -1287,6 +1306,14 @@ public static boolean isTimescaleDB(String db) {
return DATABASE_TIMESCALEDB.equals(db);
}
+ @Override
+ public boolean isQuestDB() {
+ return isQuestDB(getSQLDatabase());
+ }
+ public static boolean isQuestDB(String db) {
+ return DATABASE_QUESTDB.equals(db);
+ }
+
public boolean isIoTDB() {
return isIoTDB(getDatabase());
@@ -1487,7 +1514,7 @@ public String getTablePath() {
String q = getQuote();
String ns = isSurrealDB() ? getSQLNamespace() : null;
- String cl = isPostgreSQL() || isCockroachDB() || isOpenGauss() || isDuckDB() ? getSQLCatalog() : null;
+ String cl = isPSQL() ? getSQLCatalog() : null;
String sch = getSQLSchema();
String sqlTable = getSQLTable();
@@ -1715,6 +1742,274 @@ else if (SQL_FUNCTION_MAP.containsKey(method) == false) {
return method + parseSQLExpression(KEY_HAVING, expression.substring(start), containRaw, false, null);
}
+ @Override
+ public String getSample() {
+ return sample;
+ }
+ public AbstractSQLConfig setSample(String... conditions) {
+ return setSample(StringUtil.getString(conditions));
+ }
+ @Override
+ public AbstractSQLConfig setSample(String sample) {
+ this.sample = sample;
+ return this;
+ }
+ @JSONField(serialize = false)
+ public String getSampleString(boolean hasPrefix) {
+ //加上子表的 sample
+ String joinSample = "";
+ if (joinList != null) {
+ boolean first = true;
+ for (Join j : joinList) {
+ if (j.isAppJoin()) {
+ continue;
+ }
+
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getSample() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
+
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ // if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ // }
+ String c = ((AbstractSQLConfig) cfg).getSampleString(false);
+
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinSample += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
+ }
+ }
+
+ String sample = StringUtil.getTrimedString(getSample());
+
+ String[] keys = StringUtil.split(sample);
+ if (keys == null || keys.length <= 0) {
+ return StringUtil.isEmpty(joinSample, true) ? "" : (hasPrefix ? " SAMPLE BY " : "") + joinSample;
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String item = keys[i];
+ //if ("fill(null)".equals(item) || "fill(linear)".equals(item) || "fill(prev)".equals(item) || "fill(previous)".equals(item)) {
+ // continue;
+ //}
+
+ String origin = item;
+
+ if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
+ //这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
+ if (StringUtil.isNumberOrAlpha(origin) == false) {
+ throw new IllegalArgumentException("预编译模式下 @sample:value 中 " + item + " 不合法! value 里面用 , 分割的"
+ + "每一项必须是 column 且其中 column 必须是 字母或数字组合!并且不要有多余的空格!");
+ }
+ }
+
+ keys[i] = getKey(origin);
+ }
+
+ return (hasPrefix ? " SAMPLE BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinSample, ", ");
+ }
+
+ @Override
+ public String getLatest() {
+ return latest;
+ }
+ public AbstractSQLConfig setLatest(String... conditions) {
+ return setLatest(StringUtil.getString(conditions));
+ }
+ @Override
+ public AbstractSQLConfig setLatest(String latest) {
+ this.latest = latest;
+ return this;
+ }
+ @JSONField(serialize = false)
+ public String getLatestString(boolean hasPrefix) {
+ //加上子表的 latest
+ String joinLatest = "";
+ if (joinList != null) {
+ boolean first = true;
+ for (Join j : joinList) {
+ if (j.isAppJoin()) {
+ continue;
+ }
+
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getLatest() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
+
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ // if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ // }
+ String c = ((AbstractSQLConfig) cfg).getLatestString(false);
+
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinLatest += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
+ }
+ }
+
+ String latest = StringUtil.getTrimedString(getLatest());
+
+ String[] keys = StringUtil.split(latest);
+ if (keys == null || keys.length <= 0) {
+ return StringUtil.isEmpty(joinLatest, true) ? "" : (hasPrefix ? " LATEST ON " : "") + joinLatest;
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String item = keys[i];
+ String origin = item;
+
+ if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
+ //这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
+ if (StringUtil.isName(origin) == false) {
+ throw new IllegalArgumentException("预编译模式下 @latest:value 中 " + item + " 不合法! value 里面用 , 分割的"
+ + "每一项必须是 column 且其中 column 必须是 英语单词!并且不要有多余的空格!");
+ }
+ }
+
+ keys[i] = getKey(origin);
+ }
+
+ return (hasPrefix ? " LATEST ON " : "") + StringUtil.concat(StringUtil.getString(keys), joinLatest, ", ");
+ }
+
+ @Override
+ public String getPartition() {
+ return partition;
+ }
+ public AbstractSQLConfig setPartition(String... conditions) {
+ return setPartition(StringUtil.getString(conditions));
+ }
+ @Override
+ public AbstractSQLConfig setPartition(String partition) {
+ this.partition = partition;
+ return this;
+ }
+ @JSONField(serialize = false)
+ public String getPartitionString(boolean hasPrefix) {
+ //加上子表的 partition
+ String joinPartition = "";
+ if (joinList != null) {
+ boolean first = true;
+ for (Join j : joinList) {
+ if (j.isAppJoin()) {
+ continue;
+ }
+
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getPartition() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
+
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ // if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ // }
+ String c = ((AbstractSQLConfig) cfg).getPartitionString(false);
+
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinPartition += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
+ }
+ }
+
+ String partition = StringUtil.getTrimedString(getPartition());
+
+ String[] keys = StringUtil.split(partition);
+ if (keys == null || keys.length <= 0) {
+ return StringUtil.isEmpty(joinPartition, true) ? "" : (hasPrefix ? " PARTITION BY " : "") + joinPartition;
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String item = keys[i];
+ String origin = item;
+
+ if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
+ //这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
+ if (StringUtil.isName(origin) == false) {
+ throw new IllegalArgumentException("预编译模式下 @partition:value 中 " + item + " 不合法! value 里面用 , 分割的"
+ + "每一项必须是 column 且其中 column 必须是 英语单词!并且不要有多余的空格!");
+ }
+ }
+
+ keys[i] = getKey(origin);
+ }
+
+ return (hasPrefix ? " PARTITION BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinPartition, ", ");
+ }
+
+ @Override
+ public String getFill() {
+ return fill;
+ }
+ public AbstractSQLConfig setFill(String... conditions) {
+ return setFill(StringUtil.getString(conditions));
+ }
+ @Override
+ public AbstractSQLConfig setFill(String fill) {
+ this.fill = fill;
+ return this;
+ }
+ @JSONField(serialize = false)
+ public String getFillString(boolean hasPrefix) {
+ //加上子表的 fill
+ String joinFill = "";
+ if (joinList != null) {
+ boolean first = true;
+ for (Join j : joinList) {
+ if (j.isAppJoin()) {
+ continue;
+ }
+
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getFill() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
+
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ // if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ // }
+ String c = ((AbstractSQLConfig) cfg).getFillString(false);
+
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinFill += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
+ }
+ }
+
+ String fill = StringUtil.getTrimedString(getFill());
+
+ String[] keys = StringUtil.split(fill);
+ if (keys == null || keys.length <= 0) {
+ return StringUtil.isEmpty(joinFill, true) ? "" : (hasPrefix ? " FILL(" : "") + joinFill + ")";
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String item = keys[i];
+ String origin = item;
+
+ if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
+ //这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
+ if (StringUtil.isName(origin) == false) {
+ throw new IllegalArgumentException("预编译模式下 @fill:value 中 " + item + " 不合法! value 里面用 , 分割的"
+ + "每一项必须是 column 且其中 column 必须是 英语单词!并且不要有多余的空格!");
+ }
+ }
+
+ keys[i] = getKey(origin);
+ }
+
+ return (hasPrefix ? " FILL(" : "") + StringUtil.concat(StringUtil.getString(keys), joinFill, ", ") + ")";
+ }
+
@Override
public String getOrder() {
return order;
@@ -2741,35 +3036,38 @@ public static int getOffset(int page, int count) {
public String getLimitString() {
int count = getCount();
- if (isMilvus()) {
+ boolean isSurrealDB = isSurrealDB();
+ boolean isQuestDB = isQuestDB();
+ if (isSurrealDB || isQuestDB || isMilvus()) {
if (count == 0) {
Parser parser = getParser();
count = parser == null ? AbstractParser.MAX_QUERY_COUNT : parser.getMaxQueryCount();
}
int offset = getOffset(getPage(), count);
- return " LIMIT " + offset + ", " + count; // 目前 moql-transx 的限制
- } else if (isSurrealDB()) {
- if (count == 0) {
- Parser parser = getParser();
- count = parser == null ? AbstractParser.MAX_QUERY_COUNT : parser.getMaxQueryCount();
+ if (isQuestDB()) {
+ return " LIMIT " + offset + ", " + (offset + count);
+ }
+ else if (isSurrealDB()) {
+ return " START " + offset + " LIMIT " + count;
+ }
+ else {
+ return " LIMIT " + offset + ", " + count; // 目前 moql-transx 的限制
}
-
- int offset = getOffset(getPage(), count);
- return " START " + offset + " LIMIT " + count;
}
if (count <= 0 || RequestMethod.isHeadMethod(getMethod(), true)) { // TODO HEAD 真的不需要 LIMIT ?
return "";
}
+ boolean isOracle = isOracle();
return getLimitString(
- getPage()
- , getCount()
- , isOracle() || isSQLServer() || isDb2()
- , isOracle() || isDameng() || isKingBase()
- , isPresto() || isTrino()
- );
+ getPage()
+ , count
+ , isTSQL()
+ , isOracle || isDameng() || isKingBase()
+ , isPresto() || isTrino()
+ );
}
/**获取限制数量及偏移量
* @param page
@@ -3462,6 +3760,7 @@ protected String concatJoinWhereString(String whereString) throws Exception {
case "^": // SIDE JOIN: ! (A & B)
case "(": // ANTI JOIN: A & ! B
case ")": // FOREIGN JOIN: B & ! A
+ case "~": // ASOF JOIN: B ~= A
jc = j.getJoinConfig();
boolean isMain = jc.isMain();
jc.setMain(false).setPrepared(isPrepared()).setPreparedValueList(new ArrayList());
@@ -3547,7 +3846,7 @@ else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
throw new UnsupportedOperationException(
"join:value 中 value 里的 " + jt + "/" + j.getPath()
+ "错误!不支持 " + jt + " 等 [ @ APP, < LEFT, > RIGHT, * CROSS"
- + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN ] 之外的 JOIN 类型 !"
+ + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN, ~ ASOF ] 之外的 JOIN 类型 !"
);
}
}
@@ -3986,7 +4285,7 @@ public String getRegExpString(String key, String column, Object[] values, int ty
*/
@JSONField(serialize = false)
public String getRegExpString(String key, String column, String value, boolean ignoreCase) {
- if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
+ if (isPSQL()) {
return getKey(column) + " ~" + (ignoreCase ? "* " : " ") + getValue(key, column, value);
}
if (isOracle() || isDameng() || isKingBase() || (isMySQL() && getDBVersionNums()[0] >= 8)) {
@@ -4312,7 +4611,7 @@ public String getContainString(String key, String column, Object[] childs, int t
}
condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR));
- if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
+ if (isPSQL()) {
condition += (getKey(column) + " @> " + getValue(key, column, newJSONArray(c)));
// operator does not exist: jsonb @> character varying "[" + c + "]");
}
@@ -4763,11 +5062,15 @@ private static String getConditionString(String table, AbstractSQLConfig config)
String aggregation;
if (RequestMethod.isGetMethod(config.getMethod(), true)) {
aggregation = config.getGroupString(true) + config.getHavingString(true)
+ + config.getSampleString(true) + config.getLatestString(true)
+ + config.getPartitionString(true) + config.getFillString(true)
+ config.getOrderString(true);
}
else if (RequestMethod.isHeadMethod(config.getMethod(), true)) {
// TODO 加参数 isPagenation 判断是 GET 内分页 query:2 查总数,不用加这些条件
- aggregation = config.getGroupString(true) + config.getHavingString(true) ;
+ aggregation = config.getGroupString(true) + config.getHavingString(true)
+ + config.getSampleString(true) + config.getLatestString(true)
+ + config.getPartitionString(true) + config.getFillString(true);
}
else if (config.getMethod() == PUT || config.getMethod() == DELETE) {
aggregation = config.getHavingString(true) ;
@@ -4890,12 +5193,16 @@ public String getJoinString() throws Exception {
sql = " INNER JOIN " + jc.getTablePath();
sql = concatJoinOn(sql, quote, j, jt, onList);
break;
+ case "~": // ASOF JOIN: B ~= A
+ sql = " ASOF JOIN " + jc.getTablePath();
+ sql = concatJoinOn(sql, quote, j, jt, onList);
+ break;
default:
String k = jc.getTableKey();
throw new UnsupportedOperationException(
"join:value 中 value 里的 " + k + "/" + j.getPath()
+ "错误!不支持 " + k + " 等 [ @ APP, < LEFT, > RIGHT, * CROSS"
- + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN ] 之外的 JOIN 类型 !"
+ + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN, ~ ASOF ] 之外的 JOIN 类型 !"
);
}
@@ -5018,7 +5325,7 @@ else if (l > 0 && StringUtil.isName(String.valueOf(l))) {
}
else if (rt.endsWith("~")) {
boolean ignoreCase = "*~".equals(rt);
- if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
+ if (isPSQL()) {
sql += (first ? ON : AND) + lk + (isNot ? NOT : "") + " ~" + (ignoreCase ? "* " : " ") + rk;
}
else if (isOracle() || isDameng() || isKingBase()) {
@@ -5073,7 +5380,7 @@ else if ("{}".equals(rt) || "<>".equals(rt)) {
String arrKeyPath = isIn ? rk : lk;
String itemKeyPath = isIn ? lk : rk;
- if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
+ if (isPSQL()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
sql += (first ? ON : AND) + (isNot ? "( " : "") + getCondition(isNot, arrKeyPath
+ " IS NOT NULL AND " + arrKeyPath + " @> " + itemKeyPath) + (isNot ? ") " : "");
}
@@ -5308,6 +5615,10 @@ else if (userId instanceof Subquery) {}
String group = request.getString(KEY_GROUP);
Object having = request.get(KEY_HAVING);
String havingAnd = request.getString(KEY_HAVING_AND);
+ String sample = request.getString(KEY_SAMPLE);
+ String latest = request.getString(KEY_LATEST);
+ String partition = request.getString(KEY_PARTITION);
+ String fill = request.getString(KEY_FILL);
String order = request.getString(KEY_ORDER);
Object keyMap = request.get(KEY_KEY);
String raw = request.getString(KEY_RAW);
@@ -5337,6 +5648,10 @@ else if (userId instanceof Subquery) {}
request.remove(KEY_GROUP);
request.remove(KEY_HAVING);
request.remove(KEY_HAVING_AND);
+ request.remove(KEY_SAMPLE);
+ request.remove(KEY_LATEST);
+ request.remove(KEY_PARTITION);
+ request.remove(KEY_FILL);
request.remove(KEY_ORDER);
request.remove(KEY_KEY);
request.remove(KEY_RAW);
@@ -5841,6 +6156,10 @@ else if (keyMap != null) {
config.setGroup(group);
config.setHaving(havingMap);
config.setHavingCombine(havingCombine);
+ config.setSample(sample);
+ config.setLatest(latest);
+ config.setPartition(partition);
+ config.setFill(fill);
config.setOrder(order);
String[] jsons = StringUtil.split(json);
@@ -5905,6 +6224,18 @@ else if (keyMap != null) {
if (havingAnd != null) {
request.put(KEY_HAVING_AND, havingAnd);
}
+ if (sample != null) {
+ request.put(KEY_SAMPLE, sample);
+ }
+ if (latest != null) {
+ request.put(KEY_LATEST, latest);
+ }
+ if (partition != null) {
+ request.put(KEY_PARTITION, partition);
+ }
+ if (fill != null) {
+ request.put(KEY_FILL, fill);
+ }
if (order != null) {
request.put(KEY_ORDER, order);
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 9393f43be..2f0482091 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1185,12 +1185,16 @@ public PreparedStatement getStatement(@NotNull SQLConfig config, String sql)
}
}
else if (RequestMethod.isGetMethod(config.getMethod(), true)) {
- //if (config.isPresto() || config.isTrino()) {
+ // if (config.isPresto() || config.isTrino()) {
// statement = getConnection(config).prepareStatement(sql); // , ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
- //} else {
+ // } else {
// statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
- //}
- if (config.isMySQL() || config.isPostgreSQL() || config.isCockroachDB() || config.isOracle() || config.isSQLServer() || config.isDb2()) {
+ // }
+
+ // TODO 补充各种支持 TYPE_SCROLL_SENSITIVE 和 CONCUR_UPDATABLE 的数据库
+ if (config.isMySQL() || config.isTiDB() || config.isMariaDB() || config.isOracle() || config.isSQLServer() || config.isDb2()
+ || config.isPostgreSQL() || config.isCockroachDB() || config.isOpenGauss() || config.isTimescaleDB() || config.isQuestDB()
+ ) {
statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
} else {
statement = getConnection(config).prepareStatement(sql);
diff --git a/APIJSONORM/src/main/java/apijson/orm/Join.java b/APIJSONORM/src/main/java/apijson/orm/Join.java
index f648ff8bf..449208bd9 100644
--- a/APIJSONORM/src/main/java/apijson/orm/Join.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Join.java
@@ -19,7 +19,7 @@ public class Join {
private String path; // /User/id@
- private String joinType; // "@" - APP, "<" - LEFT, ">" - RIGHT, "*" - CROSS, "&" - INNER, "|" - FULL, "!" - OUTER, "^" - SIDE, "(" - ANTI, ")" - FOREIGN
+ private String joinType; // "@" - APP, "<" - LEFT, ">" - RIGHT, "*" - CROSS, "&" - INNER, "|" - FULL, "!" - OUTER, "^" - SIDE, "(" - ANTI, ")" - FOREIGN, "~" ASOF
private String table; // User
private String alias; // owner
private int count = 1; // 当app join子表,需要返回子表的行数,默认1行;
@@ -143,6 +143,9 @@ public boolean isAntiJoin() {
public boolean isForeignJoin() {
return ")".equals(getJoinType());
}
+ public boolean isAsofJoin() {
+ return "~".equals(getJoinType());
+ }
public boolean isLeftOrRightJoin() {
String jt = getJoinType();
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 64e6273ee..ac541c8da 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -40,6 +40,7 @@ public interface SQLConfig {
String DATABASE_INFLUXDB = "INFLUXDB"; // https://www.influxdata.com/products/influxdb-overview
String DATABASE_TDENGINE = "TDENGINE"; // https://tdengine.com
String DATABASE_TIMESCALEDB = "TIMESCALEDB"; // https://www.timescale.com
+ String DATABASE_QUESTDB = "QUESTDB"; // https://questdb.com
String DATABASE_IOTDB = "IOTDB"; // https://iotdb.apache.org/zh/UserGuide/latest/API/Programming-JDBC.html
String DATABASE_REDIS = "REDIS"; // https://redisql.com
@@ -77,6 +78,10 @@ public interface SQLConfig {
SQLConfig setTag(String tag);
+ boolean isTSQL();
+ boolean isMSQL();
+ boolean isPSQL();
+
boolean isMySQL();
boolean isPostgreSQL();
boolean isSQLServer();
@@ -100,6 +105,7 @@ public interface SQLConfig {
boolean isInfluxDB();
boolean isTDengine();
boolean isTimescaleDB();
+ boolean isQuestDB();
boolean isIoTDB();
boolean isRedis();
boolean isMongoDB();
@@ -317,6 +323,18 @@ default int[] getDBVersionNums() {
String getHavingCombine();
SQLConfig setHavingCombine(String havingCombine);
+ String getSample();
+ SQLConfig setSample(String order);
+
+ String getLatest();
+ SQLConfig setLatest(String latest);
+
+ String getPartition();
+ SQLConfig setPartition(String partition);
+
+ String getFill();
+ SQLConfig setFill(String fill);
+
String getOrder();
SQLConfig setOrder(String order);
diff --git a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
index 9dab4ea16..0acced3be 100755
--- a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
+++ b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
@@ -229,6 +229,9 @@ else if (config.isTDengine()) {
else if (config.isTimescaleDB()) {
db = SQLConfig.DATABASE_TIMESCALEDB + " " + dbVersion;
}
+ else if (config.isQuestDB()) {
+ db = SQLConfig.DATABASE_QUESTDB + " " + dbVersion;
+ }
else if (config.isIoTDB()) {
db = SQLConfig.DATABASE_IOTDB + " " + dbVersion;
}
From d8b8e5783fc632043b25d89b818aaf25f24c4d24 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 15:45:08 +0800
Subject: [PATCH 15/91] =?UTF-8?q?QuestDB:=20=E8=A7=A3=E5=86=B3=20JOIN=20?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=E4=B8=BB=E8=A1=A8=E7=A9=BA=E5=AF=B9=E8=B1=A1?=
=?UTF-8?q?=E5=B9=B6=E6=9C=AA=E8=BF=94=E5=9B=9E=E5=89=AF=E8=A1=A8=EF=BC=8C?=
=?UTF-8?q?=E8=A7=A3=E5=86=B3=20QuestDB=20=E8=87=AA=E5=8A=A8=E6=8A=8A?=
=?UTF-8?q?=E5=89=AF=E8=A1=A8=20id=20=E7=AD=89=E4=B8=8E=E4=B8=BB=E8=A1=A8?=
=?UTF-8?q?=E9=87=8D=E5=90=8D=E5=AD=97=E6=AE=B5=E6=94=B9=E6=88=90=20id1=20?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLExecutor.java | 235 ++++++++++--------
1 file changed, 137 insertions(+), 98 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 2f0482091..49f51438e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -198,6 +198,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
ResultSet rs = null;
List resultList = null;
Map childMap = null;
+ Map keyMap = null;
try {
if (unknownType) {
@@ -393,6 +394,8 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
// 直接用数组存取更快 Map columnIndexAndJoinMap = isExplain || ! hasJoin ? null : new HashMap<>(length);
Join[] columnIndexAndJoinMap = isExplain || ! hasJoin ? null : new Join[length];
+ Map repeatMap = columnIndexAndJoinMap == null || ! config.isQuestDB() ? null : new HashMap<>();
+ keyMap = repeatMap == null ? null : new HashMap<>();
// int viceColumnStart = length + 1; //第一个副表字段的index
@@ -431,34 +434,69 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
List column = config.getColumn();
int mainColumnSize = column == null ? 0 : column.size();
- // FIXME 主副表同名导致主表数据当成副表数据 { "[]": { "join": "": 0 }, "Comment:to": { "@column": "id,content", "id@": "/Comment/toId" } }, "@explain": true }
boolean toFindJoin = mainColumnSize <= 0 || i > mainColumnSize; // 主表就不用找 JOIN 配置
if (StringUtil.isEmpty(sqlTable, true)) {
+ //sqlTable = null;
+
if (toFindJoin) { // 在主表字段数量内的都归属主表
long startTime3 = System.currentTimeMillis();
sqlTable = rsmd.getTableName(i); // SQL 函数甚至部分字段都不返回表名,当然如果没传 @column 生成的 Table.* 则返回的所有字段都会带表名
//if (StringUtil.isEmpty(sqlTable, true)) {
- // boolean isEmpty = curItem == null || curItem.isEmpty();
- String label = getKey(config, rs, rsmd, index, curItem, i, childMap);
- if (i > 1 && ( (curItem != null && curItem.containsKey(label))
- || (StringUtil.isNotEmpty(label) && StringUtil.equals(label, curConfig == null ? null : curConfig.getIdKey())))
+ // boolean isEmpty = curItem == null || curItem.isEmpty();
+ String key = getKey(config, rs, rsmd, index, curItem, i, childMap, keyMap);
+ char last = repeatMap == null ? 0 : key.charAt(key.length() - 1);
+ String repeatKey = last < '0' || last > '9' ? null : key.substring(0, key.length() - 1);
+ Integer repeatCount = repeatKey == null ? null : repeatMap.get(repeatKey);
+ int nc = repeatCount == null ? 1 : repeatCount + 1;
+ if (last == nc + '0') {
+ keyMap.put(key, repeatKey);
+ repeatMap.put(repeatKey, nc);
+ key = repeatKey; // QuestDB 会自动把副表与主表同名的字段重命名,例如 id 改为 id1, date 改为 date1
+ }
+
+ if (i > 1 && ( (curItem != null && curItem.containsKey(key))
+ || (StringUtil.isNotEmpty(key) && StringUtil.equals(key, curConfig == null ? null : curConfig.getIdKey())))
) { // Presto 等引擎 JDBC 返回 rsmd.getTableName(i) 为空,主表如果一个字段都没有会导致 APISJON 主副表所有字段都不返回
sqlTable = null;
if (reseted) {
- lastViceTableStart ++;
-
SQLConfig lastCfg = lastJoin == null ? null : lastJoin.getCacheConfig();
List lastColumn = lastCfg == null ? null : lastCfg.getColumn();
+
+ lastViceTableStart ++;
lastViceColumnStart += lastColumn == null ? 1 : lastColumn.size();
}
+ else if (isMain) {
+ for (int j = 0; j < joinList.size(); j++) {
+ Join join = joinList.get(j);
+ SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
+ List c = cfg == null ? null : cfg.getColumn();
+
+ if (cfg != null) {
+ sqlTable = cfg.getSQLTable();
+ sqlAlias = cfg.getAlias();
+ lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
+ lastViceColumnStart = i + 1;
+
+ curJoin = join;
+ curConfig = cfg;
+ curColumn = c;
+
+ toFindJoin = false;
+ isMain = false;
+ break;
+ }
+ }
+ }
+
reseted = true;
}
//}
sqlResultDuration += System.currentTimeMillis() - startTime3;
- if (StringUtil.isEmpty(sqlTable, true)) { // hasJoin 已包含这个判断 && joinList != null) {
+ if (toFindJoin && StringUtil.isEmpty(sqlTable, true)) { // hasJoin 已包含这个判断 && joinList != null) {
+ //sqlTable = null; // QuestDB 等 rsmd.getTableName(i) 返回 "" 导致以下 StringUtil.equalsIgnoreCase 对比失败
int nextViceColumnStart = lastViceColumnStart; // 主表没有 @column 时会偏小 lastViceColumnStart
int joinCount = joinList.size();
@@ -469,11 +507,11 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
nextViceColumnStart += (c != null && ! c.isEmpty() ?
c.size() : (
- StringUtil.equalsIgnoreCase(sqlTable, lastTableName)
+ StringUtil.equalsIgnoreCase(sqlTable, lastTableName)
&& StringUtil.equals(sqlAlias, lastAliasName) ? 1 : 0
- )
- );
- if (i < nextViceColumnStart || j >= joinCount - 1) {
+ )
+ );
+ if (i < nextViceColumnStart) { // 导致只 JOIN 一张副表时主表数据放到副表 || j >= joinCount - 1) {
sqlTable = cfg.getSQLTable();
sqlAlias = cfg.getAlias();
lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
@@ -496,8 +534,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
toFindJoin = false;
}
}
- }
- else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWith("\""))){
+ } else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWith("\""))){
sqlTable = sqlTable.substring(1, sqlTable.length() - 1);
}
@@ -512,7 +549,7 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
if (cfg != null && StringUtil.equalsIgnoreCase(sqlTable, cfg.getSQLTable())
- ) { // FIXME 导致副表字段错放到主表 && StringUtil.equals(sqlAlias, cfg.getAlias())) {
+ ) { // FIXME 导致副表字段错放到主表 && StringUtil.equals(sqlAlias, cfg.getAlias())) {
lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
curJoin = join;
@@ -621,7 +658,7 @@ else if (hasPK) {
}
}
- curItem = onPutColumn(config, rs, rsmd, index, curItem, i, curJoin, childMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
+ curItem = onPutColumn(config, rs, rsmd, index, curItem, i, curJoin, childMap, keyMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
}
if (viceItem != null) {
@@ -671,7 +708,7 @@ else if (hasPK) {
// @ APP JOIN 查询副表并缓存到 childMap <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Map> appJoinChildMap = new HashMap<>();
childMap.forEach((viceSql, item) -> appJoinChildMap.put(viceSql, Arrays.asList(item)));
- executeAppJoin(config, resultList, appJoinChildMap);
+ executeAppJoin(config, resultList, appJoinChildMap, keyMap);
// @ APP JOIN 查询副表并缓存到 childMap >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -713,7 +750,7 @@ else if (hasPK) {
* @param childMap
* @throws Exception
*/
- protected void executeAppJoin(SQLConfig config, List resultList, Map> childMap) throws Exception {
+ protected void executeAppJoin(SQLConfig config, List resultList, Map> childMap, Map keyMap) throws Exception {
List joinList = config.getJoinList();
if (joinList != null) {
@@ -737,27 +774,27 @@ protected void executeAppJoin(SQLConfig config, List resultList,
On on = onList == null || onList.isEmpty() ? null : onList.get(0); // APP JOIN 应该有且只有一个 ON 条件
String originKey = on == null ? null : on.getOriginKey();
if (originKey == null) {
- throw new NullPointerException("服务器内部错误,List 中 Join.onList[0" + (on == null ? "] = null!" : ".getOriginKey() = null!"));
+ throw new NullPointerException("服务器内部错误,List 中 Join.onList[0" + (on == null ? "] = null!" : ".getOriginKey() = null!"));
}
String key = on.getKey();
if (key == null) {
- throw new NullPointerException("服务器内部错误,List 中 Join.onList[0" + (on == null ? "] = null!" : ".getKey() = null!"));
+ throw new NullPointerException("服务器内部错误,List 中 Join.onList[0" + (on == null ? "] = null!" : ".getKey() = null!"));
}
// 取出 "id@": "@/User/userId" 中所有 userId 的值
List targetValueList = new ArrayList<>();
for (int i = 0; i < resultList.size(); i++) {
- JSONObject mainTable = resultList.get(i);
- Object targetValue = mainTable == null ? null : mainTable.get(on.getTargetKey());
+ JSONObject mainTable = resultList.get(i);
+ Object targetValue = mainTable == null ? null : mainTable.get(on.getTargetKey());
- if (targetValue != null && targetValueList.contains(targetValue) == false) {
- targetValueList.add(targetValue);
- }
+ if (targetValue != null && targetValueList.contains(targetValue) == false) {
+ targetValueList.add(targetValue);
+ }
}
if (targetValueList.isEmpty() && config.isExplain() == false) {
- throw new NotExistException("targetValueList.isEmpty() && config.isExplain() == false");
+ throw new NotExistException("targetValueList.isEmpty() && config.isExplain() == false");
}
// 替换为 "id{}": [userId1, userId2, userId3...]
@@ -796,38 +833,38 @@ protected void executeAppJoin(SQLConfig config, List resultList,
String sql2 = null;
if (childCount > 0 && isOne2Many && (jc.isMySQL() == false || jc.getDBVersionNums()[0] >= 8)) {
- // 加 row_number 字段并不会导致 count 等聚合函数统计出错,结果偏大,SQL JOIN 才会,之前没发现是因为缓存失效 bug
- // boolean noAggrFun = true;
- // List column = jc.getColumn();
- // if (column != null) {
- // for (String c : column) {
- // int start = c == null ? -1 : c.indexOf("(");
- // int end = start <= 0 ? -1 : c.lastIndexOf(")");
- // if (start > 0 && end > start) {
- // String fun = c.substring(0, start);
- // if (AbstractSQLConfig.SQL_AGGREGATE_FUNCTION_MAP.containsKey(fun)) {
- // noAggrFun = false;
- // break;
- // }
- // }
- // }
- // }
- //
- // if (noAggrFun) { // 加 row_number 字段会导致 count 等聚合函数统计出错,结果偏大?
- String q = jc.getQuote();
- sql2 = prepared && jc.isTDengine() == false ? jc.getSQL(true) : sql;
-
- String prefix = "SELECT * FROM(";
- String rnStr = ", row_number() OVER (PARTITION BY " + q + key + q + ((AbstractSQLConfig) jc).getOrderString(true) + ") _row_num_ FROM ";
- String suffix = ") _t WHERE ( (_row_num_ <= " + childCount + ") )" + (allChildCount > 0 ? " LIMIT " + allChildCount : "");
-
- sql2 = prefix
- // 放一块逻辑更清晰,也避免解析 * 等不支持或性能开销 + sql
- + sql2.replaceFirst(" FROM ", rnStr) // * 居然只能放在 row_number() 前面,放后面就报错 "SELECT ", rnStr)
- + suffix;
-
- sql = prepared ? (prefix + sql.replaceFirst(" FROM ", rnStr) + suffix) : sql2;
- // }
+ // 加 row_number 字段并不会导致 count 等聚合函数统计出错,结果偏大,SQL JOIN 才会,之前没发现是因为缓存失效 bug
+ // boolean noAggrFun = true;
+ // List column = jc.getColumn();
+ // if (column != null) {
+ // for (String c : column) {
+ // int start = c == null ? -1 : c.indexOf("(");
+ // int end = start <= 0 ? -1 : c.lastIndexOf(")");
+ // if (start > 0 && end > start) {
+ // String fun = c.substring(0, start);
+ // if (AbstractSQLConfig.SQL_AGGREGATE_FUNCTION_MAP.containsKey(fun)) {
+ // noAggrFun = false;
+ // break;
+ // }
+ // }
+ // }
+ // }
+ //
+ // if (noAggrFun) { // 加 row_number 字段会导致 count 等聚合函数统计出错,结果偏大?
+ String q = jc.getQuote();
+ sql2 = prepared && jc.isTDengine() == false ? jc.getSQL(true) : sql;
+
+ String prefix = "SELECT * FROM(";
+ String rnStr = ", row_number() OVER (PARTITION BY " + q + key + q + ((AbstractSQLConfig) jc).getOrderString(true) + ") _row_num_ FROM ";
+ String suffix = ") _t WHERE ( (_row_num_ <= " + childCount + ") )" + (allChildCount > 0 ? " LIMIT " + allChildCount : "");
+
+ sql2 = prefix
+ // 放一块逻辑更清晰,也避免解析 * 等不支持或性能开销 + sql
+ + sql2.replaceFirst(" FROM ", rnStr) // * 居然只能放在 row_number() 前面,放后面就报错 "SELECT ", rnStr)
+ + suffix;
+
+ sql = prepared ? (prefix + sql.replaceFirst(" FROM ", rnStr) + suffix) : sql2;
+ // }
}
boolean isExplain = jc.isExplain();
@@ -846,12 +883,12 @@ protected void executeAppJoin(SQLConfig config, List resultList,
try {
long executedSQLStartTime = 0;
if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
- executedSQLCount ++;
- executedSQLStartTime = System.currentTimeMillis();
+ executedSQLCount ++;
+ executedSQLStartTime = System.currentTimeMillis();
}
rs = executeQuery(jc, sql2);
if (isExplain == false) {
- executedSQLDuration += System.currentTimeMillis() - executedSQLStartTime;
+ executedSQLDuration += System.currentTimeMillis() - executedSQLStartTime;
}
int count = 0;
@@ -876,7 +913,7 @@ protected void executeAppJoin(SQLConfig config, List resultList,
JSONObject result = new JSONObject(true);
for (int i = 1; i <= length; i++) {
- result = onPutColumn(jc, rs, rsmd, index, result, i, null, null);
+ result = onPutColumn(jc, rs, rsmd, index, result, i, null, null, keyMap);
}
//每个 result 都要用新的 SQL 来存 childResultMap = onPutTable(config, rs, rsmd, childResultMap, index, result);
@@ -890,21 +927,19 @@ protected void executeAppJoin(SQLConfig config, List resultList,
List results = childMap.get(cacheSql);
if (results == null || skipMap.get(cacheSql) == null) { // 避免添加重复数据
- results = new ArrayList<>(childCount);
- childMap.put(cacheSql, results);
- skipMap.put(cacheSql, Boolean.TRUE);
+ results = new ArrayList<>(childCount);
+ childMap.put(cacheSql, results);
+ skipMap.put(cacheSql, Boolean.TRUE);
}
if (childCount <= 0 || results.size() < childCount) { // 避免超过子数组每页数量
- // if (count == 1 && results.isEmpty() == false) { // 避免添加重复数据
- // results.clear();
- // }
- results.add(result); //缓存到 childMap
- count ++;
- Log.d(TAG, ">>> executeAppJoin childMap.put('" + cacheSql + "', result); childMap.size() = " + childMap.size());
+ // if (count == 1 && results.isEmpty() == false) { // 避免添加重复数据
+ // results.clear();
+ // }
+ results.add(result); //缓存到 childMap
+ count ++;
+ Log.d(TAG, ">>> executeAppJoin childMap.put('" + cacheSql + "', result); childMap.size() = " + childMap.size());
}
- }
- }
finally {
if (rs != null) {
try {
@@ -939,29 +974,24 @@ protected void executeAppJoin(SQLConfig config, List resultList,
* @throws Exception
*/
protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
- , final int tablePosition, @NotNull JSONObject table, final int columnIndex, Join join, Map childMap) throws Exception {
+ , final int row, @NotNull JSONObject table, final int columnIndex, Join join, Map childMap
+ , Map keyMap) throws Exception {
if (table == null) { // 对应副表 viceSql 不能生成正常 SQL, 或者是 ! - Outer, ( - ANTI JOIN 的副表这种不需要缓存及返回的数据
Log.i(TAG, "onPutColumn table == null >> return table;");
return table;
}
- if (isHideColumn(config, rs, rsmd, tablePosition, table, columnIndex, childMap)) {
- Log.i(TAG, "onPutColumn isHideColumn(config, rs, rsmd, tablePosition, table, columnIndex, childMap) >> return table;");
+ if (isHideColumn(config, rs, rsmd, row, table, columnIndex, childMap, keyMap)) {
+ Log.i(TAG, "onPutColumn isHideColumn(config, rs, rsmd, row, table, columnIndex, childMap) >> return table;");
return table;
}
- String label = getKey(config, rs, rsmd, tablePosition, table, columnIndex, childMap);
- Object value = getValue(config, rs, rsmd, tablePosition, table, columnIndex, label, childMap);
+ String label = getKey(config, rs, rsmd, row, table, columnIndex, childMap, keyMap);
+ Object value = getValue(config, rs, rsmd, row, table, columnIndex, label, childMap, keyMap);
// 主表必须 put 至少一个 null 进去,否则全部字段为 null 都不 put 会导致中断后续正常返回值
- if (value != null) {
+ if (value != null || ENABLE_OUTPUT_NULL_COLUMN || (join == null && table.isEmpty())) {
table.put(label, value);
- } else {
- if (join == null && table.isEmpty()) {
- table.put(label, null);
- } else if (ENABLE_OUTPUT_NULL_COLUMN) {
- table.put(label, null);
- }
}
return table;
@@ -971,7 +1001,7 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSe
* @param config
* @param rs
* @param rsmd
- * @param tablePosition
+ * @param row
* @param table
* @param columnIndex
* @param childMap
@@ -979,7 +1009,8 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSe
* @throws SQLException
*/
protected boolean isHideColumn(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
- , final int tablePosition, @NotNull JSONObject table, final int columnIndex, Map childMap) throws SQLException {
+ , final int row, @NotNull JSONObject table, final int columnIndex, Map childMap
+ , Map keyMap) throws SQLException {
return rsmd.getColumnName(columnIndex).startsWith("_");
}
@@ -999,12 +1030,11 @@ protected List onPutTable(@NotNull SQLConfig config, @NotNull Res
return resultList;
}
-
-
protected String getKey(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
- , final int tablePosition, @NotNull JSONObject table, final int columnIndex, Map childMap) throws Exception {
+ , final int row, @NotNull JSONObject table, final int columnIndex, Map childMap
+ , Map keyMap) throws Exception {
long startTime = System.currentTimeMillis();
- String key = rsmd.getColumnLabel(columnIndex); // dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
+ String key = rsmd.getColumnLabel(columnIndex); // dotIndex < 0 ? label : label.substring(dotIndex + 1);
sqlResultDuration += System.currentTimeMillis() - startTime;
if (config.isHive()) {
@@ -1018,18 +1048,26 @@ protected String getKey(@NotNull SQLConfig config, @NotNull ResultSet rs, @No
}
}
+ if (keyMap != null && ! keyMap.isEmpty()) {
+ String nk = keyMap.get(key);
+ if (StringUtil.isNotEmpty(nk, true)) {
+ key = nk; // QuestDB 会自动把副表与主表同名的字段重命名,例如 id 改为 id1, date 改为 date1
+ }
+ }
+
return key;
}
protected Object getValue(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
- , final int tablePosition, @NotNull JSONObject table, final int columnIndex, String lable, Map childMap) throws Exception {
+ , final int row, @NotNull JSONObject table, final int columnIndex, String label
+ , Map childMap, Map keyMap) throws Exception {
long startTime = System.currentTimeMillis();
Object value = rs.getObject(columnIndex);
sqlResultDuration += System.currentTimeMillis() - startTime;
// Log.d(TAG, "name:" + rsmd.getColumnName(i));
- // Log.d(TAG, "lable:" + rsmd.getColumnLabel(i));
+ // Log.d(TAG, "label:" + rsmd.getColumnLabel(i));
// Log.d(TAG, "type:" + rsmd.getColumnType(i));
// Log.d(TAG, "typeName:" + rsmd.getColumnTypeName(i));
@@ -1093,6 +1131,7 @@ else if (value instanceof Clob) { //SQL Server TEXT 类型 居然走这个
if (castToJson == false) {
List json = config.getJson();
castToJson = json != null && json.contains(lable);
+ castToJson = json != null && json.contains(label);
}
if (castToJson) {
try {
@@ -1133,13 +1172,13 @@ public Object getNumVal(Number value) {
/**判断是否为JSON类型
* @param config
- * @param lable
+ * @param label
* @param rsmd
* @param position
* @return
*/
@Override
- public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd, int position, String lable) {
+ public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd, int position, String label) {
try {
long startTime = System.currentTimeMillis();
String column = rsmd.getColumnTypeName(position);
@@ -1158,16 +1197,16 @@ public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd,
e.printStackTrace();
}
// List json = config.getJson();
- // return json != null && json.contains(lable);
+ // return json != null && json.contains(label);
return false;
}
-
@Override // 重写是为了返回类型从 Statement 改为 PreparedStatement,避免其它方法出错
public PreparedStatement getStatement(@NotNull SQLConfig config) throws Exception {
return getStatement(config, null);
}
+
@Override
public PreparedStatement getStatement(@NotNull SQLConfig config, String sql) throws Exception {
if (StringUtil.isEmpty(sql)) {
@@ -1193,7 +1232,7 @@ else if (RequestMethod.isGetMethod(config.getMethod(), true)) {
// TODO 补充各种支持 TYPE_SCROLL_SENSITIVE 和 CONCUR_UPDATABLE 的数据库
if (config.isMySQL() || config.isTiDB() || config.isMariaDB() || config.isOracle() || config.isSQLServer() || config.isDb2()
- || config.isPostgreSQL() || config.isCockroachDB() || config.isOpenGauss() || config.isTimescaleDB() || config.isQuestDB()
+ || config.isPostgreSQL() || config.isCockroachDB() || config.isOpenGauss() || config.isTimescaleDB() || config.isQuestDB()
) {
statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
} else {
From 9a45d3381e9e27095baf6367078283d7f7d98a85 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 15:46:50 +0800
Subject: [PATCH 16/91] =?UTF-8?q?QuestDB:=20=E8=A7=A3=E5=86=B3=20JOIN=20?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=E4=B8=BB=E8=A1=A8=E7=A9=BA=E5=AF=B9=E8=B1=A1?=
=?UTF-8?q?=E5=B9=B6=E6=9C=AA=E8=BF=94=E5=9B=9E=E5=89=AF=E8=A1=A8=EF=BC=8C?=
=?UTF-8?q?=E8=A7=A3=E5=86=B3=20QuestDB=20=E8=87=AA=E5=8A=A8=E6=8A=8A?=
=?UTF-8?q?=E5=89=AF=E8=A1=A8=20id=20=E7=AD=89=E4=B8=8E=E4=B8=BB=E8=A1=A8?=
=?UTF-8?q?=E9=87=8D=E5=90=8D=E5=AD=97=E6=AE=B5=E6=94=B9=E6=88=90=20id1=20?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractSQLExecutor.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 49f51438e..35d1efda2 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -940,6 +940,8 @@ protected void executeAppJoin(SQLConfig config, List resultList,
count ++;
Log.d(TAG, ">>> executeAppJoin childMap.put('" + cacheSql + "', result); childMap.size() = " + childMap.size());
}
+ }
+ }
finally {
if (rs != null) {
try {
@@ -1101,7 +1103,7 @@ else if (value instanceof Month) {
else if (value instanceof DayOfWeek) {
value = ((DayOfWeek) value).getValue();
}
- else if (value instanceof String && isJSONType(config, rsmd, columnIndex, lable)) { //json String
+ else if (value instanceof String && isJSONType(config, rsmd, columnIndex, label)) { //json String
castToJson = true;
}
else if (value instanceof Blob) { //FIXME 存的是 abcde,取出来直接就是 [97, 98, 99, 100, 101] 这种 byte[] 类型,没有经过以下处理,但最终序列化后又变成了字符串 YWJjZGU=
@@ -1130,7 +1132,7 @@ else if (value instanceof Clob) { //SQL Server TEXT 类型 居然走这个
if (castToJson == false) {
List json = config.getJson();
- castToJson = json != null && json.contains(lable);
+ castToJson = json != null && json.contains(label);
castToJson = json != null && json.contains(label);
}
if (castToJson) {
From 58404703b09c8a123a5018d31d66b2b49256cb35 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 16:18:39 +0800
Subject: [PATCH 17/91] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E8=AF=AF=E5=9C=A8?=
=?UTF-8?q?=E5=AD=90=E6=9F=A5=E8=AF=A2=E6=8B=BC=E6=8E=A5=20LIMIT=EF=BC=9B?=
=?UTF-8?q?=E8=A7=A3=E5=86=B3=20@sample=20SAMPLE=20BY=20@fill=20FILL=20?=
=?UTF-8?q?=E7=9A=84=20SQL=20=E6=8B=BC=E6=8E=A5=20bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/StringUtil.java | 15 ++++++
.../java/apijson/orm/AbstractSQLConfig.java | 47 ++++++++++---------
2 files changed, 41 insertions(+), 21 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index c6caf21e7..49c72677a 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -343,6 +343,7 @@ public static boolean isNotEmpty(String s, boolean trim) {
public static final Pattern PATTERN_PHONE;
public static final Pattern PATTERN_EMAIL;
public static final Pattern PATTERN_ID_CARD;
+ public static final Pattern PATTERN_NUM_OR_ALPHA;
public static final Pattern PATTERN_ALPHA;
public static final Pattern PATTERN_PASSWORD; //TODO
public static final Pattern PATTERN_NAME;
@@ -351,6 +352,7 @@ public static boolean isNotEmpty(String s, boolean trim) {
public static final Pattern PATTERN_BRANCH_URL;
static {
PATTERN_NUMBER = Pattern.compile("^[0-9]+$");
+ PATTERN_NUM_OR_ALPHA = Pattern.compile("^[0-9a-zA-Z_.:]+$");
PATTERN_ALPHA = Pattern.compile("^[a-zA-Z]+$");
PATTERN_ALPHA_BIG = Pattern.compile("^[A-Z]+$");
PATTERN_ALPHA_SMALL = Pattern.compile("^[a-z]+$");
@@ -442,6 +444,19 @@ public static boolean isNumberOrAlpha(String s) {
return isNumer(s) || isAlpha(s);
}
+ /**判断是否全是数字或字母
+ * @param s
+ * @return
+ */
+ public static boolean isCombineOfNumOrAlpha(String s) {
+ if (isEmpty(s, true)) {
+ return false;
+ }
+
+ currentString = s;
+ return PATTERN_NUM_OR_ALPHA.matcher(s).matches();
+ }
+
/**判断是否为代码名称,只能包含字母,数字或下划线
* @param s
* @return
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index e9b6c0a02..45383553e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1792,17 +1792,18 @@ public String getSampleString(boolean hasPrefix) {
for (int i = 0; i < keys.length; i++) {
String item = keys[i];
- //if ("fill(null)".equals(item) || "fill(linear)".equals(item) || "fill(prev)".equals(item) || "fill(previous)".equals(item)) {
- // continue;
- //}
String origin = item;
if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
//这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
- if (StringUtil.isNumberOrAlpha(origin) == false) {
+ if (StringUtil.isName(origin)) {}
+ else if (StringUtil.isCombineOfNumOrAlpha(origin)) {
+ continue;
+ }
+ else {
throw new IllegalArgumentException("预编译模式下 @sample:value 中 " + item + " 不合法! value 里面用 , 分割的"
- + "每一项必须是 column 且其中 column 必须是 字母或数字组合!并且不要有多余的空格!");
+ + "每一项必须是 column 且其中 column 必须是 数字或英语字母组合!并且不要有多余的空格!");
}
}
@@ -1994,13 +1995,21 @@ public String getFillString(boolean hasPrefix) {
for (int i = 0; i < keys.length; i++) {
String item = keys[i];
+ if ("NULL".equals(item) || "LINEAR".equals(item) || "PREV".equals(item) || "PREVIOUS".equals(item)) {
+ continue;
+ }
+
String origin = item;
if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
//这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
- if (StringUtil.isName(origin) == false) {
+ if (StringUtil.isName(origin)) {}
+ else if (StringUtil.isCombineOfNumOrAlpha(origin)) {
+ continue;
+ }
+ else {
throw new IllegalArgumentException("预编译模式下 @fill:value 中 " + item + " 不合法! value 里面用 , 分割的"
- + "每一项必须是 column 且其中 column 必须是 英语单词!并且不要有多余的空格!");
+ + "每一项必须是 column 且其中 column 必须是 数字或英语字母组合!并且不要有多余的空格!");
}
}
@@ -3035,20 +3044,26 @@ public static int getOffset(int page, int count) {
@JSONField(serialize = false)
public String getLimitString() {
int count = getCount();
+ int page = getPage();
+
+ boolean isMilvus = isMilvus();
+ if ((count <= 0 && ! (isMilvus && isMain())) || RequestMethod.isHeadMethod(getMethod(), true)) { // TODO HEAD 真的不需要 LIMIT ?
+ return "";
+ }
boolean isSurrealDB = isSurrealDB();
boolean isQuestDB = isQuestDB();
- if (isSurrealDB || isQuestDB || isMilvus()) {
+ if (isSurrealDB || isQuestDB || isMilvus) {
if (count == 0) {
Parser parser = getParser();
count = parser == null ? AbstractParser.MAX_QUERY_COUNT : parser.getMaxQueryCount();
}
- int offset = getOffset(getPage(), count);
+ int offset = getOffset(page, count);
if (isQuestDB()) {
return " LIMIT " + offset + ", " + (offset + count);
}
- else if (isSurrealDB()) {
+ else if (isSurrealDB()) {
return " START " + offset + " LIMIT " + count;
}
else {
@@ -3056,18 +3071,8 @@ else if (isSurrealDB()) {
}
}
- if (count <= 0 || RequestMethod.isHeadMethod(getMethod(), true)) { // TODO HEAD 真的不需要 LIMIT ?
- return "";
- }
-
boolean isOracle = isOracle();
- return getLimitString(
- getPage()
- , count
- , isTSQL()
- , isOracle || isDameng() || isKingBase()
- , isPresto() || isTrino()
- );
+ return getLimitString(page, count, isTSQL(), isOracle || isDameng() || isKingBase(), isPresto() || isTrino());
}
/**获取限制数量及偏移量
* @param page
From 02d90c7499b0e88a3e72d1e3df57426341aee438 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 16:28:40 +0800
Subject: [PATCH 18/91] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?=
=?UTF-8?q?=E6=8B=BC=E5=86=99=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/StringUtil.java | 14 +++++++-------
.../main/java/apijson/orm/AbstractSQLConfig.java | 4 ++--
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index 49c72677a..b357f5279 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -389,7 +389,7 @@ public static boolean isPassword(String s) {
* @return
*/
public static boolean isNumberPassword(String s) {
- return getLength(s, false) == 6 && isNumer(s);
+ return getLength(s, false) == 6 && isNumber(s);
}
/**判断email格式是否正确
* @param email
@@ -410,13 +410,13 @@ public static boolean isEmail(String email) {
* @return
*/
public static boolean isVerify(String s) {
- return getLength(s, false) >= 4 && isNumer(s);
+ return getLength(s, false) >= 4 && isNumber(s);
}
/**判断是否全是数字
* @param s
* @return
*/
- public static boolean isNumer(String s) {
+ public static boolean isNumber(String s) {
if (isNotEmpty(s, true) == false) {
return false;
}
@@ -441,7 +441,7 @@ public static boolean isAlpha(String s) {
* @return
*/
public static boolean isNumberOrAlpha(String s) {
- return isNumer(s) || isAlpha(s);
+ return isNumber(s) || isAlpha(s);
}
/**判断是否全是数字或字母
@@ -500,7 +500,7 @@ public static boolean isSmallName(String s) {
* @return
*/
public static boolean isIDCard(String number) {
- if (isNumberOrAlpha(number) == false) {
+ if (isCombineOfNumOrAlpha(number) == false) {
return false;
}
number = getString(number);
@@ -627,7 +627,7 @@ public static String getNumber(String s, boolean onlyStart) {
String single;
for (int i = 0; i < s.length(); i++) {
single = s.substring(i, i + 1);
- if (isNumer(single)) {
+ if (isNumber(single)) {
numberString.append(single);
} else {
if (onlyStart) {
@@ -732,7 +732,7 @@ public static String getPrice(String price, int formatType) {
String s;
for (int i = 0; i < price.length(); i++) {
s = price.substring(i, i + 1);
- if (".".equals(s) || isNumer(s)) {
+ if (".".equals(s) || isNumber(s)) {
correctPriceBuilder.append(s);
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 45383553e..a2882f463 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2707,7 +2707,7 @@ else if ("!=null".equals(ck)) {
if (mk.length() > 0) {
origin = mk;
}
- } else if (StringUtil.isNumer(origin)) {
+ } else if (StringUtil.isNumber(origin)) {
//do nothing
} else {
String[] keys = origin.split("[.]");
@@ -2809,7 +2809,7 @@ else if (ck.contains("`") || ck.contains("'") || origin.startsWith("_") || origi
+ " 且不包含连续减号 -- !DISTINCT 必须全大写,且后面必须有且只有 1 个空格!其它情况不允许空格!");
}
- if (StringUtil.isNumer(origin)) {
+ if (StringUtil.isNumber(origin)) {
//do nothing
} else {
String[] keys = origin.split("[.]");
From 8cdca093659a9afb9a8908cdbfb31028ce96338a Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 16:29:54 +0800
Subject: [PATCH 19/91] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?=
=?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/StringUtil.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index b357f5279..caac16d25 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -520,8 +520,6 @@ public static boolean isIDCard(String number) {
public static final String HTTP = "http";
public static final String URL_PREFIX = "http://";
public static final String URL_PREFIXs = "https://";
- public static final String URL_STAFFIX = URL_PREFIX;
- public static final String URL_STAFFIXs = URL_PREFIXs;
/**判断字符类型是否是网址
* @param url
* @return
From c9b124d7a1a8cfd48ee1f591d1d4a2ef989cf92f Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 17:17:38 +0800
Subject: [PATCH 20/91] =?UTF-8?q?=E7=AE=80=E5=8C=96=20StringUtil=20?=
=?UTF-8?q?=E4=B8=AD=E5=90=84=E7=A7=8D=E5=AD=97=E7=AC=A6=E4=B8=B2=E7=9B=B8?=
=?UTF-8?q?=E5=85=B3=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/JSON.java | 2 +-
.../src/main/java/apijson/JSONObject.java | 24 +-
.../src/main/java/apijson/JSONRequest.java | 4 +-
APIJSONORM/src/main/java/apijson/SQL.java | 2 +-
.../src/main/java/apijson/StringUtil.java | 300 +++++++++++++-----
.../main/java/apijson/orm/AbstractParser.java | 11 +-
.../java/apijson/orm/AbstractSQLConfig.java | 56 ++--
.../java/apijson/orm/AbstractSQLExecutor.java | 4 +-
.../java/apijson/orm/AbstractVerifier.java | 23 +-
.../src/main/java/apijson/orm/Logic.java | 2 +-
.../src/main/java/apijson/orm/Pair.java | 6 +-
.../src/main/java/apijson/orm/SQLConfig.java | 2 +-
12 files changed, 289 insertions(+), 147 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/JSON.java b/APIJSONORM/src/main/java/apijson/JSON.java
index d7854aae1..0a1f901b7 100755
--- a/APIJSONORM/src/main/java/apijson/JSON.java
+++ b/APIJSONORM/src/main/java/apijson/JSON.java
@@ -48,7 +48,7 @@ public static String getCorrectJson(String s) {
* @return
*/
public static String getCorrectJson(String s, boolean isArray) {
- s = StringUtil.getTrimedString(s);
+ s = StringUtil.trim(s);
// if (isArray) {
// while (s.startsWith("\"")) {
// s = s.substring(1);
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index c69a03569..0adf18365 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -326,7 +326,7 @@ public JSONObject setCache(String cache) {
* @return {@link #setColumn(String)}
*/
public JSONObject setColumn(String... keys) {
- return setColumn(StringUtil.getString(keys, true));
+ return setColumn(StringUtil.get(keys, true));
}
/**set keys need to be returned
* @param keys "key0,key1,key2..."
@@ -341,7 +341,7 @@ public JSONObject setColumn(String keys) {
* @return {@link #setNull(String)}
*/
public JSONObject setNull(String... keys) {
- return setNull(StringUtil.getString(keys, true));
+ return setNull(StringUtil.get(keys, true));
}
/**set keys whose value is null
* @param keys "key0,key1,key2..."
@@ -356,7 +356,7 @@ public JSONObject setNull(String keys) {
* @return {@link #setCast(String)}
*/
public JSONObject setCast(String... keyTypes) {
- return setCast(StringUtil.getString(keyTypes, true));
+ return setCast(StringUtil.get(keyTypes, true));
}
/**set keys and types whose value should be cast to type, cast(value AS DATE)
* @param keyTypes "key0:type0,key1:type1,key2:type2..."
@@ -371,7 +371,7 @@ public JSONObject setCast(String keyTypes) {
* @return {@link #setColumn(String)}
*/
public JSONObject setCombine(String... keys) {
- return setCombine(StringUtil.getString(keys, true));
+ return setCombine(StringUtil.get(keys, true));
}
/**set combination of keys for conditions
* @param keys key0,&key1,|key2,!key3 ... TODO or key0> | (key1{} & !key2)...
@@ -386,7 +386,7 @@ public JSONObject setCombine(String keys) {
* @return {@link #setGroup(String)}
*/
public JSONObject setGroup(String... keys) {
- return setGroup(StringUtil.getString(keys, true));
+ return setGroup(StringUtil.get(keys, true));
}
/**set keys for group by
* @param keys "key0,key1,key2..."
@@ -401,7 +401,7 @@ public JSONObject setGroup(String keys) {
* @return {@link #setHaving(String)}
*/
public JSONObject setHaving(String... keys) {
- return setHaving(StringUtil.getString(keys, true));
+ return setHaving(StringUtil.get(keys, true));
}
/**set keys for having
* @param keys "key0,key1,key2..."
@@ -423,7 +423,7 @@ public JSONObject setHaving(String keys, boolean isAnd) {
* @return {@link #setSample(String)}
*/
public JSONObject setSample(String... keys) {
- return setSample(StringUtil.getString(keys, true));
+ return setSample(StringUtil.get(keys, true));
}
/**set keys for sample by
* @param keys "key0,key1,key2..."
@@ -438,7 +438,7 @@ public JSONObject setSample(String keys) {
* @return {@link #setLatest(String)}
*/
public JSONObject setLatest(String... keys) {
- return setLatest(StringUtil.getString(keys, true));
+ return setLatest(StringUtil.get(keys, true));
}
/**set keys for latest on
* @param keys "key0,key1,key2..."
@@ -453,7 +453,7 @@ public JSONObject setLatest(String keys) {
* @return {@link #setPartition(String)}
*/
public JSONObject setPartition(String... keys) {
- return setPartition(StringUtil.getString(keys, true));
+ return setPartition(StringUtil.get(keys, true));
}
/**set keys for partition by
* @param keys key0, key1, key2 ...
@@ -468,7 +468,7 @@ public JSONObject setPartition(String keys) {
* @return {@link #setFill(String)}
*/
public JSONObject setFill(String... keys) {
- return setFill(StringUtil.getString(keys, true));
+ return setFill(StringUtil.get(keys, true));
}
/**set keys for fill(key): fill(null), fill(linear), fill(prev)
* @param keys key0, key1, key2 ...
@@ -483,7 +483,7 @@ public JSONObject setFill(String keys) {
* @return {@link #setOrder(String)}
*/
public JSONObject setOrder(String... keys) {
- return setOrder(StringUtil.getString(keys, true));
+ return setOrder(StringUtil.get(keys, true));
}
/**set keys for order by
* @param keys "key0,key1+,key2-..."
@@ -530,7 +530,7 @@ public JSONObject setJson(String keys) {
* @return {@link #puts(String, Object)}
*/
public JSONObject putsPath(String key, String... keys) {
- return puts(key+"@", StringUtil.getString(keys, "/"));
+ return puts(key+"@", StringUtil.get(keys, "/"));
}
/**
diff --git a/APIJSONORM/src/main/java/apijson/JSONRequest.java b/APIJSONORM/src/main/java/apijson/JSONRequest.java
index 62d724199..ae5e19950 100755
--- a/APIJSONORM/src/main/java/apijson/JSONRequest.java
+++ b/APIJSONORM/src/main/java/apijson/JSONRequest.java
@@ -138,7 +138,7 @@ public JSONRequest setPage(int page) {
* @return
*/
public JSONRequest setJoin(String... joins) {
- return puts(KEY_JOIN, StringUtil.getString(joins));
+ return puts(KEY_JOIN, StringUtil.get(joins));
}
/**set range for Subquery
@@ -178,7 +178,7 @@ public JSONRequest toArray(int count, int page) {
* @return {name+KEY_ARRAY : this}. if needs to be put, use {@link #putsAll(Map extends String, ? extends Object>)} instead
*/
public JSONRequest toArray(int count, int page, String name) {
- return new JSONRequest(StringUtil.getString(name) + KEY_ARRAY, this.setCount(count).setPage(page));
+ return new JSONRequest(StringUtil.get(name) + KEY_ARRAY, this.setCount(count).setPage(page));
}
diff --git a/APIJSONORM/src/main/java/apijson/SQL.java b/APIJSONORM/src/main/java/apijson/SQL.java
index 6cec79bd2..110ae3d47 100755
--- a/APIJSONORM/src/main/java/apijson/SQL.java
+++ b/APIJSONORM/src/main/java/apijson/SQL.java
@@ -242,7 +242,7 @@ public static String toLowerCase(String s) {
* @return column.isEmpty() ? "*" : column;
*/
public static String column(String column) {
- column = StringUtil.getTrimedString(column);
+ column = StringUtil.trim(column);
return column.isEmpty() ? "*" : column;
}
/**有别名的字段
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index caac16d25..13b0ff214 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -53,171 +53,317 @@ public StringUtil() {
public static final String YUAN = "元";
- private static String currentString = "";
- /**获取刚传入处理后的string
+ private static String current = "";
+ /**获取刚传入处理后的 string
+ * @must 上个影响 current 的方法 和 这个方法都应该在同一线程中,否则返回值可能不对
+ * @return
+ */
+ public static String cur() {
+ return get(current);
+ }
+
+ /**FIXME 改用 cur
* @must 上个影响currentString的方法 和 这个方法都应该在同一线程中,否则返回值可能不对
* @return
*/
+ @Deprecated
public static String getCurrentString() {
- return currentString == null ? "" : currentString;
+ return cur();
}
//获取string,为null时返回"" <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/**获取string,为null则返回""
+ * @param obj
+ * @return
+ */
+ public static String get(Object obj) {
+ return obj == null ? "" : obj.toString();
+ }
+ /**获取string,为null则返回""
+ * @param s
+ * @return
+ */
+ public static String get(String s) {
+ return s == null ? "" : s;
+ }
+ /**获取string,为null则返回""
+ * ignoreEmptyItem = false;
+ * split = ","
+ * @param arr
+ * @return {@link #get(Object[], boolean)}
+ */
+ public static String get(Object[] arr) {
+ return get(arr, false);
+ }
+ /**获取string,为null则返回""
+ * split = ","
+ * @param arr
+ * @param ignoreEmptyItem
+ * @return {@link #get(Object[], boolean)}
+ */
+ public static String get(Object[] arr, boolean ignoreEmptyItem) {
+ return get(arr, null, ignoreEmptyItem);
+ }
+ /**获取string,为null则返回""
+ * ignoreEmptyItem = false;
+ * @param arr
+ * @param split
+ * @return {@link #get(Object[], String, boolean)}
+ */
+ public static String get(Object[] arr, String split) {
+ return get(arr, split, false);
+ }
+ //CS304 Issue link: https://github.com/Tencent/APIJSON/issues/182
+ /**获取string,为null则返回""
+ * @param arr -the str arr given
+ * @param split -the token used to split
+ * @param ignoreEmptyItem -whether to ignore empty item or not
+ * @return {@link #get(Object[], String, boolean)}
+ * Here we replace the simple "+" way of concatenating with Stringbuilder 's append
+ */
+ public static String get(Object[] arr, String split, boolean ignoreEmptyItem) {
+ StringBuilder s = new StringBuilder("");
+ if (arr != null) {
+ if (split == null) {
+ split = ",";
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (ignoreEmptyItem && isEmpty(arr[i], true)) {
+ continue;
+ }
+ s.append(((i > 0 ? split : "") + arr[i]));
+ }
+ }
+ return get(s.toString());
+ }
+
+ /**FIXME 用 get 替代
* @param object
* @return
*/
+ @Deprecated
public static String getString(Object object) {
return object == null ? "" : object.toString();
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* @param cs
* @return
*/
+ @Deprecated
public static String getString(CharSequence cs) {
return cs == null ? "" : cs.toString();
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* @param s
* @return
*/
+ @Deprecated
public static String getString(String s) {
return s == null ? "" : s;
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* ignoreEmptyItem = false;
* split = ","
* @param array
- * @return {@link #getString(Object[], boolean)}
+ * @return {@link #get(Object[], boolean)}
*/
+ @Deprecated
public static String getString(Object[] array) {
- return getString(array, false);
+ return get(array, false);
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* split = ","
* @param array
* @param ignoreEmptyItem
- * @return {@link #getString(Object[], boolean)}
+ * @return {@link #get(Object[], boolean)}
*/
+ @Deprecated
public static String getString(Object[] array, boolean ignoreEmptyItem) {
- return getString(array, null, ignoreEmptyItem);
+ return get(array, null, ignoreEmptyItem);
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* ignoreEmptyItem = false;
* @param array
* @param split
- * @return {@link #getString(Object[], String, boolean)}
+ * @return {@link #get(Object[], String, boolean)}
*/
+ @Deprecated
public static String getString(Object[] array, String split) {
- return getString(array, split, false);
+ return get(array, split, false);
}
//CS304 Issue link: https://github.com/Tencent/APIJSON/issues/182
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* @param array -the str array given
* @param split -the token used to split
* @param ignoreEmptyItem -whether to ignore empty item or not
- * @return {@link #getString(Object[], String, boolean)}
+ * @return {@link #get(Object[], String, boolean)}
* Here we replace the simple "+" way of concatenating with Stringbuilder 's append
*/
+ @Deprecated
public static String getString(Object[] array, String split, boolean ignoreEmptyItem) {
- StringBuilder s = new StringBuilder("");
- if (array != null) {
- if (split == null) {
- split = ",";
- }
- for (int i = 0; i < array.length; i++) {
- if (ignoreEmptyItem && isEmpty(array[i], true)) {
- continue;
- }
- s.append(((i > 0 ? split : "") + array[i]));
- }
- }
- return getString(s.toString());
+ return get(array, split, ignoreEmptyItem);
}
//获取string,为null时返回"" >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//获取去掉前后空格后的string<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
/**获取去掉前后空格后的string,为null则返回""
+ * @param obj
+ * @return
+ */
+ public static String trim(Object obj) {
+ return trim(get(obj));
+ }
+ /**获取去掉前后空格后的string,为null则返回""
+ * @param cs
+ * @return
+ */
+ public static String trim(CharSequence cs) {
+ return trim(get(cs));
+ }
+ /**获取去掉前后空格后的string,为null则返回""
+ * @param s
+ * @return
+ */
+ public static String trim(String s) {
+ return get(s).trim();
+ }
+
+
+ /**FIXME 用 trim 替代
* @param object
* @return
*/
+ @Deprecated
public static String getTrimedString(Object object) {
- return getTrimedString(getString(object));
+ return trim(object);
}
- /**获取去掉前后空格后的string,为null则返回""
+ /**FIXME 用 trim 替代
* @param cs
* @return
*/
+ @Deprecated
public static String getTrimedString(CharSequence cs) {
- return getTrimedString(getString(cs));
+ return trim(cs);
}
- /**获取去掉前后空格后的string,为null则返回""
+ /**FIXME 用 trim 替代
* @param s
* @return
*/
+ @Deprecated
public static String getTrimedString(String s) {
- return getString(s).trim();
+ return trim(s);
}
//获取去掉前后空格后的string>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//获取去掉所有空格后的string <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
/**获取去掉所有空格后的string,为null则返回""
+ * @param obj
+ * @return
+ */
+ public static String noBlank(Object obj) {
+ return noBlank(get(obj));
+ }
+ /**获取去掉所有空格后的string,为null则返回""
+ * @param cs
+ * @return
+ */
+ public static String noBlank(CharSequence cs) {
+ return noBlank(get(cs));
+ }
+ /**获取去掉所有空格后的string,为null则返回""
+ * @param s
+ * @return
+ */
+ public static String noBlank(String s) {
+ return get(s).replaceAll("\\s", "");
+ }
+
+ /**FIXME 用 noBlank 替代
* @param object
* @return
*/
+ @Deprecated
public static String getNoBlankString(Object object) {
- return getNoBlankString(getString(object));
+ return noBlank(object);
}
- /**获取去掉所有空格后的string,为null则返回""
+ /**FIXME 用 noBlank 替代
* @param cs
* @return
*/
+ @Deprecated
public static String getNoBlankString(CharSequence cs) {
- return getNoBlankString(getString(cs));
+ return noBlank(cs);
}
- /**获取去掉所有空格后的string,为null则返回""
+ /**FIXME 用 noBlank 替代
* @param s
* @return
*/
+ @Deprecated
public static String getNoBlankString(String s) {
- return getString(s).replaceAll("\\s", "");
+ return noBlank(s);
}
//获取去掉所有空格后的string >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//获取string的长度<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
/**获取string的长度,为null则返回0
* @param object
* @param trim
* @return
*/
- public static int getLength(Object object, boolean trim) {
- return getLength(getString(object), trim);
+ public static int length(Object object, boolean trim) {
+ return length(get(object), trim);
}
/**获取string的长度,为null则返回0
* @param cs
* @param trim
* @return
*/
- public static int getLength(CharSequence cs, boolean trim) {
- return getLength(getString(cs), trim);
+ public static int length(CharSequence cs, boolean trim) {
+ return length(get(cs), trim);
}
/**获取string的长度,为null则返回0
* @param s
* @param trim
* @return
*/
+ public static int length(String s, boolean trim) {
+ s = trim ? trim(s) : s;
+ return get(s).length();
+ }
+
+
+ /**FIXME 用 length 替代
+ * @param object
+ * @param trim
+ * @return
+ */
+ @Deprecated
+ public static int getLength(Object object, boolean trim) {
+ return length(object, trim);
+ }
+ /**FIXME 用 length 替代
+ * @param cs
+ * @param trim
+ * @return
+ */
+ @Deprecated
+ public static int getLength(CharSequence cs, boolean trim) {
+ return length(cs, trim);
+ }
+ /**FIXME 用 length 替代
+ * @param s
+ * @param trim
+ * @return
+ */
+ @Deprecated
public static int getLength(String s, boolean trim) {
- s = trim ? getTrimedString(s) : s;
- return getString(s).length();
+ return length(s, trim);
}
//获取string的长度>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -238,7 +384,7 @@ public static boolean isEmpty(Object obj) {
* @return
*/
public static boolean isEmpty(Object obj, boolean trim) {
- return isEmpty(getString(obj), trim);
+ return isEmpty(get(obj), trim);
}
/**判断字符是否为空 trim = true
* @param cs
@@ -253,7 +399,7 @@ public static boolean isEmpty(CharSequence cs) {
* @return
*/
public static boolean isEmpty(CharSequence cs, boolean trim) {
- return isEmpty(getString(cs), trim);
+ return isEmpty(get(cs), trim);
}
/**判断字符是否为空 trim = true
* @param s
@@ -268,7 +414,7 @@ public static boolean isEmpty(String s) {
* @return
*/
public static boolean isEmpty(String s, boolean trim) {
- // Log.i(TAG, "getTrimedString s = " + s);
+ // Log.i(TAG, "isEmpty s = " + s);
if (s == null) {
return true;
}
@@ -279,7 +425,7 @@ public static boolean isEmpty(String s, boolean trim) {
return true;
}
- currentString = s;
+ current = s;
return false;
}
@@ -289,7 +435,7 @@ public static boolean isEmpty(String s, boolean trim) {
//判断字符是否非空 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/**判断字符是否非空 trim = true
- * @param object
+ * @param obj
* @return
*/
public static boolean isNotEmpty(Object obj) {
@@ -374,7 +520,7 @@ public static boolean isPhone(String phone) {
return false;
}
- currentString = phone;
+ current = phone;
return PATTERN_PHONE.matcher(phone).matches();
}
/**判断手机格式是否正确
@@ -382,14 +528,14 @@ public static boolean isPhone(String phone) {
* @return
*/
public static boolean isPassword(String s) {
- return getLength(s, false) >= 6 && PATTERN_PASSWORD.matcher(s).matches();
+ return length(s, false) >= 6 && PATTERN_PASSWORD.matcher(s).matches();
}
/**判断是否全是数字密码
* @param s
* @return
*/
public static boolean isNumberPassword(String s) {
- return getLength(s, false) == 6 && isNumber(s);
+ return length(s, false) == 6 && isNumber(s);
}
/**判断email格式是否正确
* @param email
@@ -400,7 +546,7 @@ public static boolean isEmail(String email) {
return false;
}
- currentString = email;
+ current = email;
return PATTERN_EMAIL.matcher(email).matches();
}
@@ -410,18 +556,18 @@ public static boolean isEmail(String email) {
* @return
*/
public static boolean isVerify(String s) {
- return getLength(s, false) >= 4 && isNumber(s);
+ return length(s, false) >= 4 && isNumber(s);
}
/**判断是否全是数字
* @param s
* @return
*/
public static boolean isNumber(String s) {
- if (isNotEmpty(s, true) == false) {
+ if (isEmpty(s, true)) {
return false;
}
- currentString = s;
+ current = s;
return PATTERN_NUMBER.matcher(s).matches();
}
/**判断是否全是字母
@@ -433,7 +579,7 @@ public static boolean isAlpha(String s) {
return false;
}
- currentString = s;
+ current = s;
return PATTERN_ALPHA.matcher(s).matches();
}
/**判断是否全是数字或字母
@@ -453,7 +599,7 @@ public static boolean isCombineOfNumOrAlpha(String s) {
return false;
}
- currentString = s;
+ current = s;
return PATTERN_NUM_OR_ALPHA.matcher(s).matches();
}
@@ -503,14 +649,14 @@ public static boolean isIDCard(String number) {
if (isCombineOfNumOrAlpha(number) == false) {
return false;
}
- number = getString(number);
+ number = get(number);
if (number.length() == 15) {
Log.i(TAG, "isIDCard number.length() == 15 old IDCard");
- currentString = number;
+ current = number;
return true;
}
if (number.length() == 18) {
- currentString = number;
+ current = number;
return true;
}
@@ -532,7 +678,7 @@ public static boolean isUrl(String url) {
return false;
}
- currentString = url;
+ current = url;
return true;
}
@@ -577,7 +723,7 @@ public static boolean isFilePath(String path) {
return false;
}
- currentString = path;
+ current = path;
return true;
}
@@ -592,14 +738,14 @@ public static boolean isFilePath(String path) {
* @return
*/
public static String getNumber(Object object) {
- return getNumber(getString(object));
+ return getNumber(get(object));
}
/**去掉string内所有非数字类型字符
* @param cs
* @return
*/
public static String getNumber(CharSequence cs) {
- return getNumber(getString(cs));
+ return getNumber(get(cs));
}
/**去掉string内所有非数字类型字符
* @param s
@@ -617,7 +763,7 @@ public static String getNumber(String s) {
* Here we replace the simple "+" way of concatenating with Stringbuilder 's append
*/
public static String getNumber(String s, boolean onlyStart) {
- if (isNotEmpty(s, true) == false) {
+ if (isEmpty(s, true)) {
return "";
}
@@ -669,7 +815,7 @@ public static String getCorrectPhone(String phone) {
return "";
}
- phone = getNoBlankString(phone);
+ phone = noBlank(phone);
phone = phone.replaceAll("-", "");
if (phone.startsWith("+86")) {
phone = phone.substring(3);
@@ -687,7 +833,7 @@ public static String getCorrectEmail(String email) {
return "";
}
- email = getNoBlankString(email);
+ email = noBlank(email);
if (isEmail(email) == false && ! email.endsWith(".com")) {
email += ".com";
}
@@ -857,7 +1003,7 @@ public static String[] split(String s, boolean trim) {
* @return
*/
public static String[] split(String s, String split, boolean trim) {
- s = getString(s);
+ s = get(s);
if (s.isEmpty()) {
return null;
}
@@ -881,7 +1027,7 @@ public static String[] split(String s, String split, boolean trim) {
* @return key + suffix,第一个字母小写
*/
public static String addSuffix(String key, String suffix) {
- key = getNoBlankString(key);
+ key = noBlank(key);
if (key.isEmpty()) {
return firstCase(suffix);
}
@@ -899,7 +1045,7 @@ public static String firstCase(String key) {
* @return
*/
public static String firstCase(String key, boolean upper) {
- key = getString(key);
+ key = get(key);
if (key.isEmpty()) {
return "";
}
@@ -923,7 +1069,7 @@ public static String toUpperCase(String s) {
* @return
*/
public static String toUpperCase(String s, boolean trim) {
- s = trim ? getTrimedString(s) : getString(s);
+ s = trim ? trim(s) : get(s);
return s.toUpperCase();
}
/**全部小写
@@ -938,7 +1084,7 @@ public static String toLowerCase(String s) {
* @return
*/
public static String toLowerCase(String s, boolean trim) {
- s = trim ? getTrimedString(s) : getString(s);
+ s = trim ? trim(s) : get(s);
return s.toLowerCase();
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index fd56b71b9..061e32d11 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -14,7 +14,6 @@
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;
@@ -819,9 +818,9 @@ public static JSONObject extendResult(JSONObject object, int code, String msg, S
object.put(JSONResponse.KEY_CODE, code);
}
- String m = StringUtil.getString(object.getString(JSONResponse.KEY_MSG));
+ String m = StringUtil.get(object.getString(JSONResponse.KEY_MSG));
if (m.isEmpty() == false) {
- msg = m + " ;\n " + StringUtil.getString(msg);
+ msg = m + " ;\n " + StringUtil.get(msg);
}
object.put(JSONResponse.KEY_MSG, msg);
@@ -1875,8 +1874,8 @@ public static String getValuePath(String parentPath, String valuePath) {
*/
public static String getAbsPath(String path, String name) {
Log.i(TAG, "getPath path = " + path + "; name = " + name + " <<<<<<<<<<<<<");
- path = StringUtil.getString(path);
- name = StringUtil.getString(name);
+ path = StringUtil.get(path);
+ name = StringUtil.get(name);
if (StringUtil.isNotEmpty(path, false)) {
if (StringUtil.isNotEmpty(name, false)) {
path += ((name.startsWith("/") ? "" : "/") + name);
@@ -1917,7 +1916,7 @@ public static String replaceArrayChildPath(String parentPath, String valuePath)
vs[i+1] = pos + "/" + vs[i+1];
}
}
- return StringUtil.getString(vs, "]/");
+ return StringUtil.get(vs, "]/");
}
}
return valuePath;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index a2882f463..4dd665edf 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1556,7 +1556,7 @@ public String getGroup() {
return group;
}
public AbstractSQLConfig setGroup(String... keys) {
- return setGroup(StringUtil.getString(keys));
+ return setGroup(StringUtil.get(keys));
}
@Override
public AbstractSQLConfig setGroup(String group) {
@@ -1593,7 +1593,7 @@ public String getGroupString(boolean hasPrefix) {
}
- group = StringUtil.getTrimedString(group);
+ group = StringUtil.trim(group);
String[] keys = StringUtil.split(group);
if (keys == null || keys.length <= 0) {
return StringUtil.isEmpty(joinGroup, true) ? "" : (hasPrefix ? " GROUP BY " : "") + joinGroup;
@@ -1610,7 +1610,7 @@ public String getGroupString(boolean hasPrefix) {
keys[i] = getKey(keys[i]);
}
- return (hasPrefix ? " GROUP BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinGroup, ", ");
+ return (hasPrefix ? " GROUP BY " : "") + StringUtil.concat(StringUtil.get(keys), joinGroup, ", ");
}
@Override
@@ -1633,7 +1633,7 @@ public AbstractSQLConfig setHaving(Map having) {
return this;
}
public AbstractSQLConfig setHaving(String... conditions) {
- return setHaving(StringUtil.getString(conditions));
+ return setHaving(StringUtil.get(conditions));
}
/**TODO @having 改为默认 | 或连接,且支持 @having: { "key1>": 1, "key{}": "length(key2)>0", "@combine": "key1,key2" }
@@ -1747,7 +1747,7 @@ public String getSample() {
return sample;
}
public AbstractSQLConfig setSample(String... conditions) {
- return setSample(StringUtil.getString(conditions));
+ return setSample(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setSample(String sample) {
@@ -1783,7 +1783,7 @@ public String getSampleString(boolean hasPrefix) {
}
}
- String sample = StringUtil.getTrimedString(getSample());
+ String sample = StringUtil.trim(getSample());
String[] keys = StringUtil.split(sample);
if (keys == null || keys.length <= 0) {
@@ -1810,7 +1810,7 @@ else if (StringUtil.isCombineOfNumOrAlpha(origin)) {
keys[i] = getKey(origin);
}
- return (hasPrefix ? " SAMPLE BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinSample, ", ");
+ return (hasPrefix ? " SAMPLE BY " : "") + StringUtil.concat(StringUtil.get(keys), joinSample, ", ");
}
@Override
@@ -1818,7 +1818,7 @@ public String getLatest() {
return latest;
}
public AbstractSQLConfig setLatest(String... conditions) {
- return setLatest(StringUtil.getString(conditions));
+ return setLatest(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setLatest(String latest) {
@@ -1854,7 +1854,7 @@ public String getLatestString(boolean hasPrefix) {
}
}
- String latest = StringUtil.getTrimedString(getLatest());
+ String latest = StringUtil.trim(getLatest());
String[] keys = StringUtil.split(latest);
if (keys == null || keys.length <= 0) {
@@ -1876,7 +1876,7 @@ public String getLatestString(boolean hasPrefix) {
keys[i] = getKey(origin);
}
- return (hasPrefix ? " LATEST ON " : "") + StringUtil.concat(StringUtil.getString(keys), joinLatest, ", ");
+ return (hasPrefix ? " LATEST ON " : "") + StringUtil.concat(StringUtil.get(keys), joinLatest, ", ");
}
@Override
@@ -1884,7 +1884,7 @@ public String getPartition() {
return partition;
}
public AbstractSQLConfig setPartition(String... conditions) {
- return setPartition(StringUtil.getString(conditions));
+ return setPartition(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setPartition(String partition) {
@@ -1920,7 +1920,7 @@ public String getPartitionString(boolean hasPrefix) {
}
}
- String partition = StringUtil.getTrimedString(getPartition());
+ String partition = StringUtil.trim(getPartition());
String[] keys = StringUtil.split(partition);
if (keys == null || keys.length <= 0) {
@@ -1942,7 +1942,7 @@ public String getPartitionString(boolean hasPrefix) {
keys[i] = getKey(origin);
}
- return (hasPrefix ? " PARTITION BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinPartition, ", ");
+ return (hasPrefix ? " PARTITION BY " : "") + StringUtil.concat(StringUtil.get(keys), joinPartition, ", ");
}
@Override
@@ -1950,7 +1950,7 @@ public String getFill() {
return fill;
}
public AbstractSQLConfig setFill(String... conditions) {
- return setFill(StringUtil.getString(conditions));
+ return setFill(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setFill(String fill) {
@@ -1986,7 +1986,7 @@ public String getFillString(boolean hasPrefix) {
}
}
- String fill = StringUtil.getTrimedString(getFill());
+ String fill = StringUtil.trim(getFill());
String[] keys = StringUtil.split(fill);
if (keys == null || keys.length <= 0) {
@@ -2016,7 +2016,7 @@ else if (StringUtil.isCombineOfNumOrAlpha(origin)) {
keys[i] = getKey(origin);
}
- return (hasPrefix ? " FILL(" : "") + StringUtil.concat(StringUtil.getString(keys), joinFill, ", ") + ")";
+ return (hasPrefix ? " FILL(" : "") + StringUtil.concat(StringUtil.get(keys), joinFill, ", ") + ")";
}
@Override
@@ -2024,7 +2024,7 @@ public String getOrder() {
return order;
}
public AbstractSQLConfig setOrder(String... conditions) {
- return setOrder(StringUtil.getString(conditions));
+ return setOrder(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setOrder(String order) {
@@ -2061,7 +2061,7 @@ public String getOrderString(boolean hasPrefix) {
}
- String order = StringUtil.getTrimedString(getOrder());
+ String order = StringUtil.trim(getOrder());
// SELECT * FROM sys.Moment ORDER BY userId ASC, rand(); 前面的 userId ASC 和后面的 rand() 都有效
// if ("rand()".equals(order)) {
// return (hasPrefix ? " ORDER BY " : "") + StringUtil.concat(order, joinOrder, ", ");
@@ -2134,7 +2134,7 @@ public String getOrderString(boolean hasPrefix) {
keys[i] = getKey(origin) + sort;
}
- return (hasPrefix ? " ORDER BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinOrder, ", ");
+ return (hasPrefix ? " ORDER BY " : "") + StringUtil.concat(StringUtil.get(keys), joinOrder, ", ");
}
@Override
@@ -2426,7 +2426,7 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
, "@column:\"column0,column1:alias1;function0(arg0,arg1,...);function1(...):alias2...\"");
}
- String c = StringUtil.getString(keys);
+ String c = StringUtil.get(keys);
c = c + (StringUtil.isEmpty(joinColumn, true) ? "" : ", " + joinColumn);//不能在这里改,后续还要用到:
return isMain() && isDistinct() ? PREFIX_DISTINCT + c : c;
default:
@@ -2471,7 +2471,7 @@ public String parseSQLExpression(String key, String expression, boolean containR
if (start < 0) {
//没有函数 ,可能是字段,也可能是 DISTINCT xx
String[] cks = parseArgsSplitWithComma(expression, true, containRaw, allowAlias);
- expression = StringUtil.getString(cks);
+ expression = StringUtil.get(cks);
} else { // FIXME 用括号断开? 如果少的话,用关键词加括号断开,例如 )OVER( 和 )AGAINST(
// 窗口函数 rank() OVER (PARTITION BY id ORDER BY userId ASC)
// 全文索引 math(name,tag) AGAINST ('a b +c -d' IN NATURALE LANGUAGE MODE) // IN BOOLEAN MODE
@@ -2547,7 +2547,7 @@ public String parseSQLExpression(String key, String expression, boolean containR
+ " 中 ?value 必须符合正则表达式 " + PATTERN_RANGE + " 且不包含连续减号 -- 或注释符 /* !不允许多余的空格!");
}
- String origin = fun + "(" + (distinct ? PREFIX_DISTINCT : "") + StringUtil.getString(ckeys) + ")" + suffix;
+ String origin = fun + "(" + (distinct ? PREFIX_DISTINCT : "") + StringUtil.get(ckeys) + ")" + suffix;
expression = origin + (StringUtil.isEmpty(alias, true) ? "" : getAs() + quote + alias + quote);
}
else {
@@ -2604,8 +2604,8 @@ else if (SQL_FUNCTION_MAP.containsKey(fun) == false) {
// 获取后半部分的参数解析 (agr0 agr1 ...)
String argsString2[] = parseArgsSplitWithComma(argString2, false, containRaw, allowAlias);
- expression = fun + "(" + StringUtil.getString(agrsString1) + (containOver ? ") OVER (" : ") AGAINST (")
- + StringUtil.getString(argsString2) + ")" + suffix // 传参不传空格,拼接带空格
+ expression = fun + "(" + StringUtil.get(agrsString1) + (containOver ? ") OVER (" : ") AGAINST (")
+ + StringUtil.get(argsString2) + ")" + suffix // 传参不传空格,拼接带空格
+ (StringUtil.isEmpty(alias, true) ? "" : getAs() + quote + alias + quote);
}
}
@@ -2867,7 +2867,7 @@ public String getValuesString() {
}
items[i] += ")";
}
- s = StringUtil.getString(items);
+ s = StringUtil.get(items);
}
return s;
}
@@ -3367,7 +3367,7 @@ protected String parseCombineExpression(RequestMethod method, String quote, Stri
, Map conditionMap, String combine, boolean verifyName, boolean containRaw, boolean isHaving) throws Exception {
String errPrefix = table + (isHaving ? ":{ @having:{ " : ":{ ") + "@combine:'" + combine + (isHaving ? "' } }" : "' }");
- String s = StringUtil.getString(combine);
+ String s = StringUtil.get(combine);
if (s.startsWith(" ") || s.endsWith(" ") ) {
throw new IllegalArgumentException(errPrefix + " 中字符 '" + s
+ "' 不合法!不允许首尾有空格,也不允许连续空格!空格不能多也不能少!"
@@ -5463,7 +5463,7 @@ public static SQLConfig newSQLConfig(RequestMethod method,
String database = request.getString(KEY_DATABASE);
if (StringUtil.isEmpty(database, false) == false && DATABASE_LIST.contains(database) == false) {
throw new UnsupportedDataTypeException("@database:value 中 value 错误,只能是 ["
- + StringUtil.getString(DATABASE_LIST.toArray()) + "] 中的一种!");
+ + StringUtil.get(DATABASE_LIST.toArray()) + "] 中的一种!");
}
String datasource = request.getString(KEY_DATASOURCE);
@@ -5748,7 +5748,7 @@ else if (userId instanceof Subquery) {}
}
column = (id == null ? "" : idKey + ",") + (userId == null ? "" : userIdKey + ",")
- + StringUtil.getString(columns); //set已经判断过不为空
+ + StringUtil.get(columns); //set已经判断过不为空
int idCount = id == null ? (userId == null ? 0 : 1) : (userId == null ? 1 : 2);
int size = idCount + columns.length; // 以 key 数量为准
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 35d1efda2..838f5a8a8 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -190,8 +190,8 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
Log.d(TAG, "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
+ "\n已生成 " + generatedSQLCount + " 条 SQL"
+ "\nexecute startTime = " + startTime
- + "\ndatabase = " + StringUtil.getString(config.getDatabase())
- + "; schema = " + StringUtil.getString(config.getSchema())
+ + "\ndatabase = " + StringUtil.get(config.getDatabase())
+ + "; schema = " + StringUtil.get(config.getSchema())
+ "; sql = \n" + sql
+ "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index b2eca8bc8..e4fb2526f 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -34,8 +34,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import apijson.orm.script.JavaScriptExecutor;
-import apijson.orm.script.ScriptExecutor;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
@@ -69,7 +67,6 @@
import apijson.orm.model.TestRecord;
import javax.script.ScriptEngine;
-import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
/**校验器(权限、请求参数、返回结果等)
@@ -295,7 +292,7 @@ public boolean verifyAccess(SQLConfig config) throws Exception {
if (ROLE_MAP.containsKey(role) == false) {
Set NAMES = ROLE_MAP.keySet();
throw new IllegalArgumentException("角色 " + role + " 不存在!" +
- "只能是[" + StringUtil.getString(NAMES.toArray()) + "]中的一种!");
+ "只能是[" + StringUtil.get(NAMES.toArray()) + "]中的一种!");
}
if (role.equals(UNKNOWN) == false) { //未登录的角色
@@ -435,7 +432,7 @@ else if (id instanceof String) {
Object oid;
for (List ovl : ovs) {
oid = ovl == null || index >= ovl.size() ? null : ovl.get(index);
- if (oid == null || StringUtil.getString(oid).equals("" + visitorId) == false) {
+ if (oid == null || StringUtil.get(oid).equals("" + visitorId) == false) {
throw new IllegalAccessException(visitorIdKey + " = " + oid + " 的 " + table
+ " 不允许 " + role + " 用户的 " + method.name() + " 请求!");
}
@@ -459,7 +456,7 @@ else if (id instanceof String) {
}
else {
requestId = config.getWhere(visitorIdKey, true);//JSON里数值不能保证是Long,可能是Integer
- if (requestId != null && StringUtil.getString(requestId).equals(StringUtil.getString(visitorId)) == false) {
+ if (requestId != null && StringUtil.get(requestId).equals(StringUtil.get(visitorId)) == false) {
throw new IllegalAccessException(visitorIdKey + " = " + requestId + " 的 " + table
+ " 不允许 " + role + " 用户的 " + method.name() + " 请求!");
}
@@ -934,11 +931,11 @@ public static JSONObject parse(@NotNull final RequestMethod m
JSONObject update = target.getJSONObject(UPDATE.name());
JSONObject replace = target.getJSONObject(REPLACE.name());
- String exist = StringUtil.getString(target.getString(EXIST.name()));
- String unique = StringUtil.getString(target.getString(UNIQUE.name()));
- String remove = StringUtil.getString(target.getString(REMOVE.name()));
- String must = StringUtil.getString(target.getString(MUST.name()));
- String refuse = StringUtil.getString(target.getString(REFUSE.name()));
+ String exist = StringUtil.get(target.getString(EXIST.name()));
+ String unique = StringUtil.get(target.getString(UNIQUE.name()));
+ String remove = StringUtil.get(target.getString(REMOVE.name()));
+ String must = StringUtil.get(target.getString(MUST.name()));
+ String refuse = StringUtil.get(target.getString(REFUSE.name()));
Object _if = target.get(IF.name());
boolean ifIsStr = _if instanceof String && StringUtil.isNotEmpty(_if, true);
@@ -952,7 +949,7 @@ public static JSONObject parse(@NotNull final RequestMethod m
// Object code = target.get(CODE.name());
- String allowPartialUpdateFail = StringUtil.getString(target.getString(ALLOW_PARTIAL_UPDATE_FAIL.name()));
+ String allowPartialUpdateFail = StringUtil.get(target.getString(ALLOW_PARTIAL_UPDATE_FAIL.name()));
// 移除字段<<<<<<<<<<<<<<<<<<<
@@ -1097,7 +1094,7 @@ public static JSONObject parse(@NotNull final RequestMethod m
for (String rk : rkset) {
if (refuseSet.contains(rk)) { // 不允许的字段
throw new IllegalArgumentException(method + "请求," + name
- + " 里面不允许传 " + rk + " 等" + StringUtil.getString(refuseSet) + "内的任何字段!");
+ + " 里面不允许传 " + rk + " 等" + StringUtil.get(refuseSet) + "内的任何字段!");
}
if (rk == null) { // 无效的key
diff --git a/APIJSONORM/src/main/java/apijson/orm/Logic.java b/APIJSONORM/src/main/java/apijson/orm/Logic.java
index cfc08d016..bb8e806e6 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Logic.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Logic.java
@@ -42,7 +42,7 @@ public Logic(int type) {
}
public Logic(String key) {
this.originKey = key;
- key = StringUtil.getString(key);
+ key = StringUtil.get(key);
int type = getType(key.isEmpty() ? "" : key.substring(key.length() - 1));
diff --git a/APIJSONORM/src/main/java/apijson/orm/Pair.java b/APIJSONORM/src/main/java/apijson/orm/Pair.java
index a1f471c42..71ed7eb2c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Pair.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Pair.java
@@ -79,7 +79,7 @@ public static String toPairString(String typeKey, String valueKey) {
* @return
*/
public static String toPairString(Class> type, Object value) {
- return toPairString(type == null ? null : type.getSimpleName(), StringUtil.getString(value));
+ return toPairString(type == null ? null : type.getSimpleName(), StringUtil.get(value));
}
/**
@@ -109,7 +109,7 @@ public static Entry parseEntry(String pair, boolean isRightValue
* @return @NonNull
*/
public static Entry parseEntry(String pair, boolean isRightValueDefault, String defaultValue) {
- pair = StringUtil.getString(pair);//让客户端去掉所有空格 getNoBlankString(pair);
+ pair = StringUtil.get(pair);//让客户端去掉所有空格 getNoBlankString(pair);
Entry entry = new Entry();
if (pair.isEmpty() == false) {
int index = pair.indexOf(":");
@@ -137,7 +137,7 @@ public static Entry parseVariableEntry(String pair) {
* @return
*/
public static Entry, Object> parseVariableEntry(String pair, Map valueMap) {
- pair = StringUtil.getString(pair);//让客户端去掉所有空格 getNoBlankString(pair);
+ pair = StringUtil.get(pair);//让客户端去掉所有空格 getNoBlankString(pair);
Entry, Object> entry = new Entry, Object>();
if (pair.isEmpty() == false) {
int index = pair.contains(":") ? pair.indexOf(":") : -1;
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index ac541c8da..e86fd0fc0 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -151,7 +151,7 @@ public interface SQLConfig {
@NotNull
default int[] getDBVersionNums() {
- String dbVersion = StringUtil.getNoBlankString(getDBVersion());
+ String dbVersion = StringUtil.noBlank(getDBVersion());
if (dbVersion.isEmpty()) {
return new int[]{0};
}
From 970aee59fed8de155a92dae77af8ff82b06c42c3 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 17:54:09 +0800
Subject: [PATCH 21/91] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?=
=?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 838f5a8a8..2165c3b05 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1133,7 +1133,6 @@ else if (value instanceof Clob) { //SQL Server TEXT 类型 居然走这个
if (castToJson == false) {
List json = config.getJson();
castToJson = json != null && json.contains(label);
- castToJson = json != null && json.contains(label);
}
if (castToJson) {
try {
From dd374314a0fd5a82d9c8170f11c028f4a57fd103 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 23 Mar 2025 23:05:10 +0800
Subject: [PATCH 22/91] =?UTF-8?q?APIJSON=20=E2=80=93=20The=20No-Code=20API?=
=?UTF-8?q?=20Revolution=20That=20Puts=20Developers=20in=20the=20Fast=20La?=
=?UTF-8?q?ne?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
thank you, [Hazem Abbas](https://medevel.com/author/hazem/), for posting this article ~
https://medevel.com/apijson
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 67610d168..4dc10db9e 100644
--- a/README.md
+++ b/README.md
@@ -622,6 +622,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[wend看源码-ORM-APIJSON](https://itwend.blog.csdn.net/article/details/143980281)
+[APIJSON – The No-Code API Revolution That Puts Developers in the Fast Lane](https://medevel.com/apijson)
+
### 生态项目
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
From 6712e9602cd1a2db95667a3c7cbd29e35233ef66 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 30 Mar 2025 05:16:12 +0800
Subject: [PATCH 23/91] =?UTF-8?q?=E5=8E=BB=E9=99=A4=20fastjson=EF=BC=8CJSO?=
=?UTF-8?q?NObject=20=E7=94=A8=20M=20extends=20Map=20?=
=?UTF-8?q?=E6=9B=BF=E4=BB=A3=EF=BC=8CJSONArray=20=E7=94=A8=20L=20extends?=
=?UTF-8?q?=20List=20=E6=9B=BF=E4=BB=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 5 -
APIJSONORM/src/main/java/apijson/JSON.java | 670 +++++++++++++-----
.../src/main/java/apijson/JSONArray.java | 221 ++++++
.../src/main/java/apijson/JSONCreator.java | 43 ++
.../src/main/java/apijson/JSONField.java | 5 +
.../src/main/java/apijson/JSONObject.java | 145 +++-
.../src/main/java/apijson/JSONParser.java | 27 +
.../src/main/java/apijson/JSONResponse.java | 155 ++--
.../apijson/orm/AbstractFunctionParser.java | 309 +++++---
.../apijson/orm/AbstractObjectParser.java | 273 ++++---
.../main/java/apijson/orm/AbstractParser.java | 438 ++++++------
.../java/apijson/orm/AbstractSQLConfig.java | 412 +++++------
.../java/apijson/orm/AbstractSQLExecutor.java | 171 +++--
.../java/apijson/orm/AbstractVerifier.java | 322 ++++-----
.../main/java/apijson/orm/FunctionParser.java | 33 +-
.../main/java/apijson/orm/JSONRequest.java | 11 +-
.../src/main/java/apijson/orm/Join.java | 35 +-
.../main/java/apijson/orm/ObjectParser.java | 56 +-
.../java/apijson/orm/OnParseCallback.java | 10 +-
.../src/main/java/apijson/orm/Pair.java | 11 +-
.../src/main/java/apijson/orm/Parser.java | 68 +-
.../main/java/apijson/orm/ParserCreator.java | 9 +-
.../src/main/java/apijson/orm/SQLConfig.java | 118 +--
.../src/main/java/apijson/orm/SQLCreator.java | 9 +-
.../main/java/apijson/orm/SQLExecutor.java | 37 +-
.../src/main/java/apijson/orm/Subquery.java | 35 +-
.../src/main/java/apijson/orm/Verifier.java | 25 +-
.../java/apijson/orm/VerifierCreator.java | 7 +-
.../orm/exception/CommonException.java | 4 +-
.../orm/script/JSR223ScriptExecutor.java | 11 +-
.../orm/script/JavaScriptExecutor.java | 7 +-
.../apijson/orm/script/ScriptExecutor.java | 9 +-
32 files changed, 2273 insertions(+), 1418 deletions(-)
create mode 100644 APIJSONORM/src/main/java/apijson/JSONArray.java
create mode 100755 APIJSONORM/src/main/java/apijson/JSONCreator.java
create mode 100644 APIJSONORM/src/main/java/apijson/JSONField.java
create mode 100755 APIJSONORM/src/main/java/apijson/JSONParser.java
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 2cf436bd5..b4a860789 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -21,11 +21,6 @@
-
- com.alibaba
- fastjson
- 1.2.83
-
diff --git a/APIJSONORM/src/main/java/apijson/JSON.java b/APIJSONORM/src/main/java/apijson/JSON.java
index 0a1f901b7..c6573a650 100755
--- a/APIJSONORM/src/main/java/apijson/JSON.java
+++ b/APIJSONORM/src/main/java/apijson/JSON.java
@@ -4,269 +4,575 @@
package apijson;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.alibaba.fastjson.parser.Feature;
-import com.alibaba.fastjson.serializer.SerializerFeature;
-
import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+
+import apijson.orm.exception.UnsupportedDataTypeException;
+import apijson.Log;
+import apijson.StringUtil;
-/**阿里FastJSON封装类 防止解析时异常
+/**JSON工具类 防止解析时异常
* @author Lemon
*/
-public class JSON {
- private static final String TAG = "JSON";
+public interface JSON {
+ static final String TAG = "JSON";
- /**判断json格式是否正确
- * @param s
- * @return
- */
- public static boolean isJsonCorrect(String s) {
- //太长 Log.i(TAG, "isJsonCorrect <<<< " + s + " >>>>>>>");
- if (s == null
- // || s.equals("[]")
- // || s.equals("{}")
- || s.equals("")
- || s.equals("[null]")
- || s.equals("{null}")
- || s.equals("null")) {
- return false;
+ JSONParser extends Map, ? extends List> DEFAULT_JSON_PARSER = new JSONParser<>() {
+
+ @Override
+ public Map parseJSON(Object json) {
+ return Map.of();
}
- return true;
- }
- /**获取有效的json
- * @param s
- * @return
- */
- public static String getCorrectJson(String s) {
- return getCorrectJson(s, false);
- }
- /**获取有效的json
- * @param s
- * @param isArray
- * @return
- */
- public static String getCorrectJson(String s, boolean isArray) {
- s = StringUtil.trim(s);
- // if (isArray) {
- // while (s.startsWith("\"")) {
- // s = s.substring(1);
- // }
- // while (s.endsWith("\"")) {
- // s = s.substring(0, s.length() - 1);
- // }
- // }
- return s;//isJsonCorrect(s) ? s : null;
+ @Override
+ public Map parseObject(Object json) {
+ return Map.of();
+ }
+
+ @Override
+ public T parseObject(Object json, Class clazz) {
+ return null;
+ }
+
+ @Override
+ public List parseArray(Object json) {
+ return List.of();
+ }
+
+ @Override
+ public List parseArray(Object json, Class clazz) {
+ return List.of();
+ }
+
+ //
+ @Override
+ public String toJSONString(Object obj) {
+ return JSON.toJSONString(obj);
+ }
+
+ @Override
+ public Map createJSONObject() {
+ return new LinkedHashMap<>();
+ }
+
+ @Override
+ public List createJSONArray() {
+ return new ArrayList<>();
+ }
+ };
+
+ public static JSONCreator extends Map, ? extends List> DEFAULT_JSON_CREATOR = DEFAULT_JSON_PARSER;
+
+
+ public static Object parseJSON(Object json) throws Exception {
+ if (json instanceof Boolean || json instanceof Number || json instanceof Enum>) {
+ return json;
+ }
+
+ String s = StringUtil.trim(toJSONString(json));
+ if (s.startsWith("{")) {
+ return parseObject(json, DEFAULT_JSON_PARSER);
+ }
+
+ if (s.startsWith("[")) {
+ return parseArray(json, DEFAULT_JSON_PARSER);
+ }
+
+ throw new UnsupportedDataTypeException("JSON 格式错误!" + s);
}
/**
* @param json
* @return
*/
- private static final Feature[] DEFAULT_FASTJSON_FEATURES = {Feature.OrderedField, Feature.UseBigDecimal};
- public static Object parse(Object obj) {
+ public static Map parseObject(Object json) {
+ return parseObject(json, DEFAULT_JSON_PARSER);
+ }
+ public static , L extends List> M parseObject(Object json, JSONParser parser) {
+ String s = toJSONString(json);
+ if (StringUtil.isEmpty(s, true)) {
+ return null;
+ }
+
try {
- return com.alibaba.fastjson.JSON.parse(obj instanceof String ? (String) obj : toJSONString(obj), DEFAULT_FASTJSON_FEATURES);
+ M obj = parser.parseObject(s);
+ return obj;
} catch (Exception e) {
- Log.i(TAG, "parse catch \n" + e.getMessage());
+ Log.i(TAG, "parseObject catch \n" + e.getMessage());
}
return null;
}
+ public static , L extends List> T parseObject(Object json, Class clazz, JSONParser parser) {
+ String s = toJSONString(json);
+ if (StringUtil.isEmpty(s, true)) {
+ return null;
+ }
- /**obj转JSONObject
- * @param obj
- * @return
- */
- public static JSONObject parseObject(Object obj) {
- if (obj instanceof JSONObject) {
- return (JSONObject) obj;
+ try {
+ T obj = parser.parseObject(s, clazz);
+ return obj;
+ } catch (Exception e) {
+ Log.i(TAG, "parseObject catch \n" + e.getMessage());
}
- return parseObject(toJSONString(obj));
+ return null;
}
- /**json转JSONObject
+
+ /**
* @param json
* @return
*/
- public static JSONObject parseObject(String json) {
- return parseObject(json, JSONObject.class);
+ public static List parseArray(Object json) {
+ return parseArray(json, DEFAULT_JSON_PARSER);
}
- /**json转实体类
- * @param json
- * @param clazz
+
+ public static , L extends List> L parseArray(Object json, JSONParser parser) {
+ String s = toJSONString(json);
+ if (StringUtil.isEmpty(s, true)) {
+ return null;
+ }
+
+ try {
+ L arr = parser.parseArray(s);
+ return arr;
+ } catch (Exception e) {
+ Log.i(TAG, "parseArray catch \n" + e.getMessage());
+ }
+ return null;
+ }
+
+// public static > List parseArray(Object json, Class clazz) {
+// return parseArray(json, clazz, DEFAULT_JSON_PARSER);
+// }
+ public static > List parseArray(Object json, Class clazz, JSONParser> parser) {
+ String s = toJSONString(json);
+ if (StringUtil.isEmpty(s, true)) {
+ return null;
+ }
+
+ try {
+ List arr = parser.parseArray(s, clazz);
+ return arr;
+ } catch (Exception e) {
+ Log.i(TAG, "parseArray catch \n" + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * @param obj
* @return
*/
- public static T parseObject(String json, Class clazz) {
- if (clazz == null || StringUtil.isEmpty(json, true)) {
- Log.e(TAG, "parseObject clazz == null || StringUtil.isEmpty(json, true) >> return null;");
- } else {
- try {
- return com.alibaba.fastjson.JSON.parseObject(getCorrectJson(json), clazz, DEFAULT_FASTJSON_FEATURES);
- } catch (Exception e) {
- Log.i(TAG, "parseObject catch \n" + e.getMessage());
+ public static String toJSONString(Object obj) {
+ if (obj == null) {
+ return null;
+ }
+
+ // In a real implementation, you would use a JSON parser library
+ // Here we're just providing a basic implementation to replace fastjson
+ try {
+ // For now, this is a placeholder. In a real implementation,
+ // you would convert the object to a JSON string
+ if (obj instanceof String) {
+ return (String) obj;
+ }
+
+ if (obj instanceof Map) {
+ // Simple JSON object format
+ StringBuilder sb = new StringBuilder("{");
+ @SuppressWarnings("unchecked")
+ Map map = (Map) obj;
+ boolean first = true;
+ for (Map.Entry entry : map.entrySet()) {
+ if (! first) {
+ sb.append(",");
+ }
+ first = false;
+ sb.append("\"").append(entry.getKey()).append("\":");
+ Object value = entry.getValue();
+ if (value instanceof String) {
+ sb.append("\"").append(value).append("\"");
+ } else {
+ sb.append(toJSONString(value));
+ }
+ }
+ sb.append("}");
+ return sb.toString();
}
+
+ if (obj instanceof List) {
+ // Simple JSON array format
+ StringBuilder sb = new StringBuilder("[");
+ @SuppressWarnings("unchecked")
+ List list = (List) obj;
+ boolean first = true;
+ for (Object item : list) {
+ if (! first) {
+ sb.append(",");
+ }
+ first = false;
+ if (item instanceof String) {
+ sb.append("\"").append(item).append("\"");
+ } else {
+ sb.append(toJSONString(item));
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ return obj.toString();
+ }
+ catch (Exception e) {
+ Log.i(TAG, "toJSONString catch \n" + e.getMessage());
}
return null;
}
- /**list转JSONArray
- * @param list
+
+ /**判断是否为JSONObject或JSONArray的isXxx方法名
+ * @param key
* @return
*/
- public static JSONArray parseArray(List list) {
- return new JSONArray(list);
+ public static boolean isJSONType(String key) {
+ return key != null && key.startsWith("is") && key.length() > 2 && key.contains("JSON");
}
- /**obj转JSONArray
- * @param obj
- * @return
+
+ public static boolean isBooleanOrNumberOrString(Object obj) {
+ return obj instanceof Boolean || obj instanceof Number || obj instanceof String;
+ }
+
+ /**
+ * Get a value from a Map and convert to the specified type
+ * @param map Source map
+ * @param key The key
+ * @param Target type
+ * @return The converted value
*/
- public static JSONArray parseArray(Object obj) {
- if (obj instanceof JSONArray) {
- return (JSONArray) obj;
- }
- return parseArray(toJSONString(obj));
+ @SuppressWarnings("unchecked")
+ public static T get(Map map, String key) {
+ return map == null || key == null ? null : (T) map.get(key);
}
- /**json转JSONArray
- * @param json
- * @return
+
+ /**
+ * Get a value from a Map and convert to the specified type
+ * @param list Source map
+ * @param index The key
+ * @param Target type
+ * @return The converted value
*/
- public static JSONArray parseArray(String json) {
- if (StringUtil.isEmpty(json, true)) {
- Log.e(TAG, "parseArray StringUtil.isEmpty(json, true) >> return null;");
- } else {
- try {
- return com.alibaba.fastjson.JSON.parseArray(getCorrectJson(json, true));
- } catch (Exception e) {
- Log.i(TAG, "parseArray catch \n" + e.getMessage());
- }
+ @SuppressWarnings("unchecked")
+ public static T get(List list, int index) {
+ return list == null || index < 0 || index >= list.size() ? null : (T) list.get(index);
+ }
+// /**
+// * Get a value from a Map and convert to the specified type
+// * @param map Source map
+// * @param key The key
+// * @param Target type
+// * @return The converted value
+// */
+// @SuppressWarnings("unchecked")
+// public static T get(List list, int index) {
+// return list == null || index < 0 || index >= list.size() ? null : list.get(index);
+// }
+
+ /**
+ * Get a Map value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The Map value
+ * @throws UnsupportedDataTypeException If value is not a Map and cannot be converted
+ */
+ @SuppressWarnings("unchecked")
+ public static Map getMap(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
}
- return null;
+
+ if (value instanceof Map) {
+ return (Map) value;
+ }
+
+ throw new UnsupportedDataTypeException("Value for key '" + key + "' is not a Map: " + value.getClass().getName());
}
- /**JSONArray转实体类列表
- * @param array
- * @param clazz
- * @return
+
+ /**
+ * Get a List value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The List value
+ * @throws UnsupportedDataTypeException If value is not a List and cannot be converted
*/
- public static List parseArray(JSONArray array, Class clazz) {
- return parseArray(toJSONString(array), clazz);
+ @SuppressWarnings("unchecked")
+ public static List getList(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof List) {
+ return (List) value;
+ }
+
+ throw new UnsupportedDataTypeException("Value for key '" + key + "' is not a List: " + value.getClass().getName());
}
- /**json转实体类列表
- * @param json
- * @param clazz
- * @return
+
+ /**
+ * Get an int value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The int value
+ * @throws UnsupportedDataTypeException If value cannot be converted to int
*/
- public static List parseArray(String json, Class clazz) {
- if (clazz == null || StringUtil.isEmpty(json, true)) {
- Log.e(TAG, "parseArray clazz == null || StringUtil.isEmpty(json, true) >> return null;");
- } else {
+ public static Integer getInteger(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).intValue();
+ }
+
+ if (value instanceof String) {
try {
- return com.alibaba.fastjson.JSON.parseArray(getCorrectJson(json, true), clazz);
- } catch (Exception e) {
- Log.i(TAG, "parseArray catch \n" + e.getMessage());
+ return Integer.parseInt((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to int: " + e.getMessage());
}
}
- return null;
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to int");
}
- /**实体类转json
- * @param obj
- * @return
+ /**
+ * Get an int value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The int value
+ * @throws UnsupportedDataTypeException If value cannot be converted to int
*/
- public static String toJSONString(Object obj) {
- if (obj == null) {
- return null;
+ public static int getIntValue(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return 0;
}
- if (obj instanceof String) {
- return (String) obj;
+
+ if (value instanceof Number) {
+ return ((Number) value).intValue();
}
- try {
- return com.alibaba.fastjson.JSON.toJSONString(obj);
- } catch (Exception e) {
- Log.e(TAG, "toJSONString catch \n" + e.getMessage());
+
+ if (value instanceof String) {
+ try {
+ return Integer.parseInt((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to int: " + e.getMessage());
+ }
}
- return null;
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to int");
}
- /**实体类转json
- * @param obj
- * @param features
- * @return
+ /**
+ * Get an int value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The int value
+ * @throws UnsupportedDataTypeException If value cannot be converted to int
*/
- public static String toJSONString(Object obj, SerializerFeature... features) {
- if (obj == null) {
+ public static Long getLong(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
return null;
}
- if (obj instanceof String) {
- return (String) obj;
+
+ if (value instanceof Number) {
+ return ((Number) value).longValue();
}
- try {
- return com.alibaba.fastjson.JSON.toJSONString(obj, features);
- } catch (Exception e) {
- Log.e(TAG, "toJSONString catch \n" + e.getMessage());
+
+ if (value instanceof String) {
+ try {
+ return Long.parseLong((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to int: " + e.getMessage());
+ }
}
- return null;
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to int");
}
- /**格式化,显示更好看
- * @param json
- * @return
+ /**
+ * Get a long value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The long value
+ * @throws UnsupportedDataTypeException If value cannot be converted to long
*/
- public static String format(String json) {
- return format(parse(json));
+ public static long getLongValue(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return 0;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).longValue();
+ }
+
+ if (value instanceof String) {
+ try {
+ return Long.parseLong((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to long: " + e.getMessage());
+ }
+ }
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to long");
}
- /**格式化,显示更好看
- * @param object
- * @return
+
+ /**
+ * Get a double value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The double value
+ * @throws UnsupportedDataTypeException If value cannot be converted to double
*/
- public static String format(Object object) {
- return toJSONString(object, SerializerFeature.PrettyFormat);
+ public static Double getDouble(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).doubleValue();
+ }
+
+ if (value instanceof String) {
+ try {
+ return Double.parseDouble((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to double: " + e.getMessage());
+ }
+ }
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to double");
}
- /**判断是否为JSONObject
- * @param obj instanceof String ? parseObject
- * @return
+ /**
+ * Get a double value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The double value
+ * @throws UnsupportedDataTypeException If value cannot be converted to double
*/
- public static boolean isJSONObject(Object obj) {
- if (obj instanceof JSONObject) {
- return true;
+ public static double getDoubleValue(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return 0;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).doubleValue();
}
- if (obj instanceof String) {
+
+ if (value instanceof String) {
try {
- JSONObject json = parseObject((String) obj);
- return json != null && json.isEmpty() == false;
- } catch (Exception e) {
- Log.e(TAG, "isJSONObject catch \n" + e.getMessage());
+ return Double.parseDouble((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to double: " + e.getMessage());
}
}
- return false;
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to double");
}
- /**判断是否为JSONArray
- * @param obj instanceof String ? parseArray
- * @return
+
+
+ /**
+ * Get a boolean value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The boolean value
+ * @throws UnsupportedDataTypeException If value cannot be converted to boolean
*/
- public static boolean isJSONArray(Object obj) {
- if (obj instanceof JSONArray) {
- return true;
+ public static Boolean getBoolean(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
}
- if (obj instanceof String) {
- try {
- JSONArray json = parseArray((String) obj);
- return json != null && json.isEmpty() == false;
- } catch (Exception e) {
- Log.e(TAG, "isJSONArray catch \n" + e.getMessage());
+
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+
+ if (value instanceof String) {
+ String str = ((String) value).toLowerCase();
+ if (str.equals("true") || str.equals("false")) {
+ return Boolean.parseBoolean(str);
+ }
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to boolean");
+ }
+
+ if (value instanceof Number) {
+ int intValue = ((Number) value).intValue();
+ if (intValue == 0 || intValue == 1) {
+ return intValue != 0;
}
+ throw new UnsupportedDataTypeException("Cannot convert Number value '" + value + "' to boolean. Only 0 and 1 are supported.");
}
- return false;
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to boolean");
}
- /**判断是否为 Boolean,Number,String 中的一种
- * @param obj
- * @return
+ /**
+ * Get a boolean value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The boolean value
+ * @throws UnsupportedDataTypeException If value cannot be converted to boolean
*/
- public static boolean isBooleanOrNumberOrString(Object obj) {
- return obj instanceof Boolean || obj instanceof Number || obj instanceof String;
+ public static boolean getBooleanValue(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return false;
+ }
+
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+
+ if (value instanceof String) {
+ String str = ((String) value).toLowerCase();
+ if (str.equals("true") || str.equals("false")) {
+ return Boolean.parseBoolean(str);
+ }
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to boolean");
+ }
+
+ if (value instanceof Number) {
+ int intValue = ((Number) value).intValue();
+ if (intValue == 0 || intValue == 1) {
+ return intValue != 0;
+ }
+ throw new UnsupportedDataTypeException("Cannot convert Number value '" + value + "' to boolean. Only 0 and 1 are supported.");
+ }
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to boolean");
}
+ /**
+ * Get a string value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The string value
+ */
+ public static String getString(Map map, String key) {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
+ }
+
+ return value.toString();
+ }
}
diff --git a/APIJSONORM/src/main/java/apijson/JSONArray.java b/APIJSONORM/src/main/java/apijson/JSONArray.java
new file mode 100644
index 000000000..7760f6567
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/JSONArray.java
@@ -0,0 +1,221 @@
+/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+
+This source code is licensed under the Apache License Version 2.0.*/
+
+package apijson;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import apijson.orm.exception.UnsupportedDataTypeException;
+
+/**
+ * Custom JSONArray implementation based on ArrayList to replace com.alibaba.fastjson.JSONArray
+ * Maintains same API as fastjson but uses standard Java List implementation
+ * @author Lemon
+ */
+public class JSONArray extends ArrayList implements JSON {
+ private static final long serialVersionUID = 1L;
+ private static final String TAG = "JSONArray";
+
+ /**
+ * Create an empty JSONArray
+ */
+ public JSONArray() {
+ super();
+ }
+
+ /**
+ * Create a JSONArray with initial capacity
+ * @param initialCapacity the initial capacity
+ */
+ public JSONArray(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ /**
+ * Create a JSONArray from a Collection
+ * @param collection the collection to copy from
+ */
+ public JSONArray(Collection> collection) {
+ super();
+ if (collection != null) {
+ addAll(collection);
+ }
+ }
+
+ /**
+ * Create a JSONArray from a JSON string
+ * @param json JSON string
+ */
+ public JSONArray(String json) {
+ this();
+ List list = JSON.parseArray(json);
+ if (list != null) {
+ addAll(list);
+ }
+ }
+
+ /**
+ * Get a JSONObject at the specified index
+ * @param index the index
+ * @return the JSONObject or null if not a JSONObject
+ */
+ public JSONObject getJSONObject(int index) {
+ if (index < 0 || index >= size()) {
+ return null;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Map) {
+ @SuppressWarnings("unchecked")
+ Map map = (Map) obj;
+ return new JSONObject(map);
+ } else if (obj instanceof JSONObject) {
+ return (JSONObject) obj;
+ }
+ return null;
+ }
+
+ /**
+ * Get a JSONArray at the specified index
+ * @param index the index
+ * @return the JSONArray or null if not a JSONArray
+ */
+ public JSONArray getJSONArray(int index) {
+ if (index < 0 || index >= size()) {
+ return null;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof List) {
+ @SuppressWarnings("unchecked")
+ List list = (List) obj;
+ return new JSONArray(list);
+ } else if (obj instanceof List>) {
+ return (JSONArray) obj;
+ }
+ return null;
+ }
+
+ /**
+ * Get a boolean value at the specified index
+ * @param index the index
+ * @return the boolean value or false if not found
+ */
+ public boolean getBooleanValue(int index) {
+ if (index < 0 || index >= size()) {
+ return false;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Boolean) {
+ return (Boolean) obj;
+ } else if (obj instanceof Number) {
+ return ((Number) obj).intValue() != 0;
+ } else if (obj instanceof String) {
+ return Boolean.parseBoolean((String) obj);
+ }
+ return false;
+ }
+
+ /**
+ * Get an integer value at the specified index
+ * @param index the index
+ * @return the integer value or 0 if not found
+ */
+ public int getIntValue(int index) {
+ if (index < 0 || index >= size()) {
+ return 0;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Number) {
+ return ((Number) obj).intValue();
+ } else if (obj instanceof String) {
+ try {
+ return Integer.parseInt((String) obj);
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Get a long value at the specified index
+ * @param index the index
+ * @return the long value or 0 if not found
+ */
+ public long getLongValue(int index) {
+ if (index < 0 || index >= size()) {
+ return 0L;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Number) {
+ return ((Number) obj).longValue();
+ } else if (obj instanceof String) {
+ try {
+ return Long.parseLong((String) obj);
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
+ }
+ return 0L;
+ }
+
+ /**
+ * Get a double value at the specified index
+ * @param index the index
+ * @return the double value or 0 if not found
+ */
+ public double getDoubleValue(int index) {
+ if (index < 0 || index >= size()) {
+ return 0.0;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Number) {
+ return ((Number) obj).doubleValue();
+ } else if (obj instanceof String) {
+ try {
+ return Double.parseDouble((String) obj);
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
+ }
+ return 0.0;
+ }
+
+ /**
+ * Get a string value at the specified index
+ * @param index the index
+ * @return the string value or null if not found
+ */
+ public String getString(int index) {
+ if (index < 0 || index >= size()) {
+ return null;
+ }
+
+ Object obj = get(index);
+ return obj != null ? obj.toString() : null;
+ }
+
+ /**
+ * Add a value to the JSONArray
+ * @param obj the value to add
+ * @return this JSONArray
+ */
+ public JSONArray fluentAdd(Object obj) {
+ add(obj);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return JSON.toJSONString(this);
+ }
+}
\ No newline at end of file
diff --git a/APIJSONORM/src/main/java/apijson/JSONCreator.java b/APIJSONORM/src/main/java/apijson/JSONCreator.java
new file mode 100755
index 000000000..fbd9da20d
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/JSONCreator.java
@@ -0,0 +1,43 @@
+/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+
+This source code is licensed under the Apache License Version 2.0.*/
+
+
+package apijson;
+
+import apijson.orm.SQLConfig;
+import apijson.orm.SQLExecutor;
+
+import java.util.List;
+import java.util.Map;
+
+/**SQL相关创建器
+ * @author Lemon
+ */
+public interface JSONCreator, L extends List> {
+
+ @NotNull
+ M createJSONObject();
+
+// @NotNull
+// M createJSONObject(String json);
+
+ @NotNull
+ default M createJSONObject(Map m) {
+ M obj = createJSONObject();
+ obj.putAll(m);
+ return obj;
+ }
+
+ @NotNull
+ L createJSONArray();
+// @NotNull
+// L createJSONArray(String json);
+
+ @NotNull
+ default L createJSONArray(List l){
+ L arr = createJSONArray();
+ arr.addAll(l);
+ return arr;
+ }
+}
diff --git a/APIJSONORM/src/main/java/apijson/JSONField.java b/APIJSONORM/src/main/java/apijson/JSONField.java
new file mode 100644
index 000000000..11a5cc309
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/JSONField.java
@@ -0,0 +1,5 @@
+package apijson;
+
+public @interface JSONField {
+ boolean serialize() default true;
+}
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index 0adf18365..ee6828279 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -9,6 +9,10 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import apijson.orm.exception.UnsupportedDataTypeException;
/**use this class instead of com.alibaba.fastjson.JSONObject
* @author Lemon
@@ -16,7 +20,7 @@
* @see #puts
* @see #putsAll
*/
-public class JSONObject extends com.alibaba.fastjson.JSONObject {
+public class JSONObject extends LinkedHashMap implements JSON {
private static final long serialVersionUID = 1L;
private static final String TAG = "JSONObject";
@@ -25,27 +29,44 @@ public class JSONObject extends com.alibaba.fastjson.JSONObject {
/**ordered
*/
public JSONObject() {
- super(true);
+ super();
}
/**transfer Object to JSONObject
* @param object
* @see {@link #JSONObject(Object)}
*/
public JSONObject(Object object) {
- this(toJSONString(object));
+ this();
+ if (object instanceof Map) {
+ @SuppressWarnings("unchecked")
+ Map map = (Map) object;
+ putAll(map);
+ } else if (object != null) {
+ String json = JSON.toJSONString(object);
+ if (json != null) {
+ Map map = JSON.parseObject(json);
+ if (map != null) {
+ putAll(map);
+ }
+ }
+ }
}
/**parse JSONObject with JSON String
* @param json
* @see {@link #JSONObject(String)}
*/
public JSONObject(String json) {
- this(parseObject(json));
+ this();
+ Map map = JSON.parseObject(json);
+ if (map != null) {
+ putAll(map);
+ }
}
/**transfer com.alibaba.fastjson.JSONObject to JSONObject
* @param object
* @see {@link #putsAll(Map extends String, ? extends Object>)}
*/
- public JSONObject(com.alibaba.fastjson.JSONObject object) {
+ public JSONObject(Map object) {
this();
putsAll(object);
}
@@ -682,6 +703,116 @@ public Object put(String key, Object value) {
return super.put(key, value);
}
-
-
+ /**
+ * Get a boolean value from the JSONObject
+ * @param key the key
+ * @return the boolean value or false if not found
+ */
+ public boolean getBooleanValue(String key) {
+ try {
+ return JSON.getBooleanValue(this, key);
+ } catch (UnsupportedDataTypeException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Get an integer value from the JSONObject
+ * @param key the key
+ * @return the integer value or 0 if not found
+ */
+ public int getIntValue(String key) {
+ try {
+ return JSON.getIntValue(this, key);
+ } catch (UnsupportedDataTypeException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Get a long value from the JSONObject
+ * @param key the key
+ * @return the long value or 0 if not found
+ */
+ public long getLongValue(String key) {
+ try {
+ return JSON.getLongValue(this, key);
+ } catch (UnsupportedDataTypeException e) {
+ return 0L;
+ }
+ }
+
+ /**
+ * Get a double value from the JSONObject
+ * @param key the key
+ * @return the double value or 0 if not found
+ */
+ public double getDoubleValue(String key) {
+ try {
+ return JSON.getDoubleValue(this, key);
+ } catch (UnsupportedDataTypeException e) {
+ return 0.0;
+ }
+ }
+
+ /**
+ * Get a string value from the JSONObject
+ * @param key the key
+ * @return the string value or null if not found
+ */
+ public String getString(String key) {
+ Object value = get(key);
+ return value != null ? value.toString() : null;
+ }
+
+ /**
+ * Get a JSONObject value from the JSONObject
+ * @param key the key
+ * @return the JSONObject value or null if not found
+ */
+ public JSONObject getJSONObject(String key) {
+ try {
+ Map map = JSON.getMap(this, key);
+ return map != null ? new JSONObject(map) : null;
+ } catch (UnsupportedDataTypeException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get a JSONArray value from the JSONObject
+ * @param key the key
+ * @return the JSONArray value or null if not found
+ */
+ public JSONArray getJSONArray(String key) {
+ try {
+ List list = JSON.getList(this, key);
+ return list != null ? new JSONArray(list) : null;
+ } catch (UnsupportedDataTypeException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Check if the JSONObject is empty or has no values other than null
+ * @return true if empty
+ */
+ public boolean isEmpty() {
+ if (super.isEmpty()) {
+ return true;
+ }
+
+ Set> set = entrySet();
+ for (Entry entry : set) {
+ if (entry.getValue() != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return JSON.toJSONString(this);
+ }
}
diff --git a/APIJSONORM/src/main/java/apijson/JSONParser.java b/APIJSONORM/src/main/java/apijson/JSONParser.java
new file mode 100755
index 000000000..1199ef5d5
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/JSONParser.java
@@ -0,0 +1,27 @@
+/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+
+This source code is licensed under the Apache License Version 2.0.*/
+
+
+package apijson;
+
+import java.util.List;
+import java.util.Map;
+
+/**SQL相关创建器
+ * @author Lemon
+ */
+public interface JSONParser, L extends List> extends JSONCreator {
+
+ M parseJSON(Object json);
+
+ M parseObject(Object json);
+
+ T parseObject(Object json, Class clazz);
+
+ L parseArray(Object json);
+
+ List parseArray(Object json, Class clazz);
+
+ String toJSONString(Object obj);
+}
diff --git a/APIJSONORM/src/main/java/apijson/JSONResponse.java b/APIJSONORM/src/main/java/apijson/JSONResponse.java
index 21f3fe8f6..7ee4f9f41 100755
--- a/APIJSONORM/src/main/java/apijson/JSONResponse.java
+++ b/APIJSONORM/src/main/java/apijson/JSONResponse.java
@@ -5,11 +5,11 @@
package apijson;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
+import apijson.orm.exception.UnsupportedDataTypeException;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+
+import static apijson.JSON.parseObject;
/**parser for response
* @author Lemon
@@ -19,7 +19,7 @@
*