diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index f656d471..f3d63376 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON-spring-boot3
- 6.2.0
+ 6.4.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index a1fe3425..bc127594 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 = "6.2.1";
+ public static final String VERSION = "6.4.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/RequestMethod.java b/APIJSONORM/src/main/java/apijson/RequestMethod.java
index 410775c1..875200b7 100755
--- a/APIJSONORM/src/main/java/apijson/RequestMethod.java
+++ b/APIJSONORM/src/main/java/apijson/RequestMethod.java
@@ -83,6 +83,14 @@ public static boolean isHeadMethod(RequestMethod method, boolean containPrivate)
public static boolean isQueryMethod(RequestMethod method) {
return isGetMethod(method, true) || isHeadMethod(method, true);
}
+
+ /**是否为更新(增删改)的请求方法
+ * @param method
+ * @return 读操作(GET型或HEAD型) - false, 写操作(POST,PUT,DELETE) - true
+ */
+ public static boolean isUpdateMethod(RequestMethod method) {
+ return ! isQueryMethod(method);
+ }
/**是否为开放(不限制请求的结构或内容;明文,浏览器能直接访问及查看)的请求方法
* @param method
@@ -92,6 +100,14 @@ public static boolean isPublicMethod(RequestMethod method) {
return method == null || method == GET || method == HEAD;
}
+ /**是否为私有(限制请求的结构或内容)的请求方法
+ * @param method
+ * @return
+ */
+ public static boolean isPrivateMethod(RequestMethod method) {
+ return ! isPublicMethod(method);
+ }
+
public static String getName(RequestMethod method) {
return method == null ? GET.name() : method.name();
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 0b8c8607..bfb3b1e1 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -157,7 +157,7 @@ public abstract class AbstractSQLConfig implements SQLConfig\\|\\[\\]\\{\\} /\\.\\+\\-\\*\\^\\?\\(\\)\\$]+$");
- TABLE_KEY_MAP = new HashMap();
+ TABLE_KEY_MAP = new HashMap<>();
TABLE_KEY_MAP.put(Table.class.getSimpleName(), Table.TABLE_NAME);
TABLE_KEY_MAP.put(Column.class.getSimpleName(), Column.TABLE_NAME);
TABLE_KEY_MAP.put(PgClass.class.getSimpleName(), PgClass.TABLE_NAME);
@@ -195,9 +195,15 @@ public abstract class AbstractSQLConfig implements SQLConfig(); // 保证顺序,避免配置冲突等意外情况
@@ -1189,6 +1195,14 @@ public static boolean isPresto(String db) {
return DATABASE_PRESTO.equals(db);
}
+ @Override
+ public boolean isTrino() {
+ return isTrino(getSQLDatabase());
+ }
+ public static boolean isTrino(String db) {
+ return DATABASE_TRINO.equals(db);
+ }
+
@Override
public boolean isSnowflake() {
return isSnowflake(getSQLDatabase());
@@ -1214,11 +1228,11 @@ public static boolean isCassandra(String db) {
}
@Override
- public boolean isTrino() {
- return isTrino(getSQLDatabase());
+ public boolean isMilvus() {
+ return isMilvus(getSQLDatabase());
}
- public static boolean isTrino(String db) {
- return DATABASE_TRINO.equals(db);
+ public static boolean isMilvus(String db) {
+ return DATABASE_MILVUS.equals(db);
}
@Override
@@ -1245,12 +1259,28 @@ public static boolean isRedis(String db) {
return DATABASE_REDIS.equals(db);
}
+ @Override
+ public boolean isMongoDB() {
+ return isMongoDB(getSQLDatabase());
+ }
+ public static boolean isMongoDB(String db) {
+ return DATABASE_MONGODB.equals(db);
+ }
+
+ @Override
+ public boolean isKafka() {
+ return isKafka(getSQLDatabase());
+ }
+ public static boolean isKafka(String db) {
+ return DATABASE_KAFKA.equals(db);
+ }
+
@Override
public boolean isMQ() {
return isMQ(getSQLDatabase());
}
public static boolean isMQ(String db) {
- return DATABASE_MQ.equals(db);
+ return DATABASE_MQ.equals(db) || isKafka(db);
}
@Override
@@ -1258,7 +1288,7 @@ public String getQuote() {
if(isElasticsearch()) {
return "";
}
- return isMySQL() || isMariaDB() || isTiDB() || isClickHouse() || isTDengine() ? "`" : "\"";
+ return isMySQL() || isMariaDB() || isTiDB() || isClickHouse() || isTDengine() || isMilvus() ? "`" : "\"";
}
public String quote(String s) {
@@ -1272,6 +1302,7 @@ public String getSchema() {
}
@NotNull
+ @Override
public String getSQLSchema() {
String table = getTable();
//强制,避免因为全局默认的 @schema 自动填充进来,导致这几个类的 schema 为 sys 等其它值
@@ -1953,14 +1984,14 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
}
// 简单点, 后台配置就带上 AS
- // int index = expression.lastIndexOf(":");
- // String alias = expression.substring(index+1);
- // boolean hasAlias = StringUtil.isName(alias);
- // String pre = index > 0 && hasAlias ? expression.substring(0, index) : expression;
- // if (RAW_MAP.containsValue(pre) || "".equals(RAW_MAP.get(pre))) { // newSQLConfig 提前处理好的
- // expression = pre + (hasAlias ? " AS " + alias : "");
- // continue;
- // }
+ int index = expression.lastIndexOf(":");
+ String alias = expression.substring(index+1);
+ boolean hasAlias = StringUtil.isName(alias);
+ String pre = index > 0 && hasAlias ? expression.substring(0, index) : expression;
+ if (RAW_MAP.containsValue(pre) || "".equals(RAW_MAP.get(pre))) { // newSQLConfig 提前处理好的
+ keys[i] = pre + (hasAlias ? " AS " + alias : "");
+ continue;
+ }
}
if (expression.length() > 100) {
@@ -2001,6 +2032,16 @@ public String parseSQLExpression(String key, String expression, boolean containR
* @return
*/
public String parseSQLExpression(String key, String expression, boolean containRaw, boolean allowAlias, String example) {
+ if (containRaw) {
+ String s = RAW_MAP.get(expression);
+ if ("".equals(s)) {
+ return expression;
+ }
+ if (s != null) {
+ return s;
+ }
+ }
+
String quote = getQuote();
int start = expression.indexOf('(');
if (start < 0) {
@@ -2578,9 +2619,22 @@ public static int getOffset(int page, int count) {
*/
@JSONField(serialize = false)
public String getLimitString() {
- if (count <= 0 || RequestMethod.isHeadMethod(getMethod(), true)) {
+ int count = getCount();
+
+ if (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 的限制
+ }
+
+ if (count <= 0 || RequestMethod.isHeadMethod(getMethod(), true)) { // TODO HEAD 真的不需要 LIMIT ?
return "";
}
+
return getLimitString(
getPage()
, getCount()
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index 79c1d119..b2eca8bc 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -80,7 +80,7 @@
public abstract class AbstractVerifier implements Verifier, IdCallback {
private static final String TAG = "AbstractVerifier";
- /**为 PUT, DELETE 强制要求必须有 id/id{} 条件
+ /**为 PUT, DELETE 强制要求必须有 id/id{}/id{}@ 条件
*/
public static boolean IS_UPDATE_MUST_HAVE_ID_CONDITION = true;
/**开启校验请求角色权限
@@ -700,8 +700,9 @@ public JSONObject onParseJSONObject(String key, JSONObject tobj, JSONObject robj
throw new IllegalArgumentException(method + "请求," + name + "/" + key + " 不能传 " + finalIdKey + " !");
}
} else {
- if (RequestMethod.isQueryMethod(method) == false) {
- verifyId(method.name(), name, key, robj, finalIdKey, maxUpdateCount, IS_UPDATE_MUST_HAVE_ID_CONDITION);
+ Boolean atLeastOne = tobj == null ? null : tobj.getBoolean(Operation.IS_ID_CONDITION_MUST.name());
+ if (Boolean.TRUE.equals(atLeastOne) || RequestMethod.isUpdateMethod(method)) {
+ verifyId(method.name(), name, key, robj, finalIdKey, maxUpdateCount, atLeastOne != null ? atLeastOne : IS_UPDATE_MUST_HAVE_ID_CONDITION);
String userIdKey = idCallback == null ? null : idCallback.getUserIdKey(db, sh, ds, key);
String finalUserIdKey = StringUtil.isEmpty(userIdKey, false) ? apijson.JSONObject.KEY_USER_ID : userIdKey;
@@ -746,7 +747,7 @@ private static void verifyId(@NotNull String method, @NotNull String name, @NotN
Object id = robj.get(idKey); //如果必须传 id ,可在Request表中配置NECESSARY
if (id != null && id instanceof Number == false && id instanceof String == false) {
throw new IllegalArgumentException(method + "请求," + name + "/" + key
- + " 里面的 " + idKey + ":value 中value的类型只能是 Long 或 String !");
+ + " 里面的 " + idKey + ":value 中value的类型只能是 Long 或 String !");
}
@@ -795,7 +796,7 @@ else if (o instanceof String) {
}
else {
throw new IllegalArgumentException(method + "请求," + name + "/" + key
- + " 里面的 " + idInKey + ":[] 中所有项的类型都只能是 Long 或 String !");
+ + " 里面的 " + idInKey + ":[] 中所有项的类型都只能是 Long 或 String !");
}
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/Operation.java b/APIJSONORM/src/main/java/apijson/orm/Operation.java
index 6a588a94..2976d09b 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Operation.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Operation.java
@@ -145,8 +145,13 @@ public enum Operation {
/**
* 允许批量增删改部分失败,结构是
* "Table[],key[],key:alias[]"
- * 自动 ALLOW_PARTIAL_UPDATE_FAILED_TABLE_MAP.put
+ * 自动 ALLOW_PARTIAL_UPDATE_FAILED_TABLE_MAP.put,结构是 Boolean,例如 true
*/
- ALLOW_PARTIAL_UPDATE_FAIL;
+ ALLOW_PARTIAL_UPDATE_FAIL,
+
+ /**
+ * 强制要求必须有 id/id{}/id{}@ 条件,结构是 Boolean,例如 true
+ */
+ IS_ID_CONDITION_MUST;
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index a453d9a8..7372630c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -34,10 +34,13 @@ public interface SQLConfig {
String DATABASE_SNOWFLAKE = "SNOWFLAKE"; // https://www.snowflake.com
String DATABASE_DATABRICKS = "DATABRICKS"; // https://www.databricks.com
String DATABASE_CASSANDRA = "CASSANDRA"; // https://cassandra.apache.org
+ 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_REDIS = "REDIS";
- String DATABASE_MQ = "MQ";
+ String DATABASE_REDIS = "REDIS"; // https://redisql.com
+ String DATABASE_MONGODB = "MONGODB"; // https://www.mongodb.com/docs/atlas/data-federation/query/query-with-sql
+ String DATABASE_KAFKA = "KAFKA"; // https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-Kafka
+ String DATABASE_MQ = "MQ"; //
String SCHEMA_INFORMATION = "information_schema"; //MySQL, PostgreSQL, SQL Server 都有的系统模式
String SCHEMA_SYS = "sys"; //SQL Server 系统模式
@@ -50,19 +53,19 @@ public interface SQLConfig {
Parser getParser();
- AbstractSQLConfig setParser(Parser parser);
+ SQLConfig setParser(Parser parser);
ObjectParser getObjectParser();
- AbstractSQLConfig setObjectParser(ObjectParser objectParser);
+ SQLConfig setObjectParser(ObjectParser objectParser);
int getVersion();
- AbstractSQLConfig setVersion(int version);
+ SQLConfig setVersion(int version);
String getTag();
- AbstractSQLConfig setTag(String tag);
+ SQLConfig setTag(String tag);
boolean isMySQL();
boolean isPostgreSQL();
@@ -77,17 +80,20 @@ public interface SQLConfig {
boolean isClickHouse();
boolean isHive();
boolean isPresto();
+ boolean isTrino();
boolean isSnowflake();
boolean isDatabricks();
boolean isCassandra();
- boolean isTrino();
+ boolean isMilvus();
boolean isInfluxDB();
boolean isTDengine();
boolean isRedis();
+ boolean isMongoDB();
+ boolean isKafka();
boolean isMQ();
- //暂时只兼容以上几种
+ // 暂时只兼容以上几种
// boolean isSQL();
// boolean isTSQL();
// boolean isPLSQL();
@@ -215,6 +221,7 @@ default int[] getDBVersionNums() {
String getDatabase();
SQLConfig setDatabase(String database);
+ String getSQLSchema();
String getSchema();
SQLConfig setSchema(String schema);
@@ -243,7 +250,7 @@ default int[] getDBVersionNums() {
String getTablePath();
Map getKeyMap();
- AbstractSQLConfig setKeyMap(Map keyMap);
+ SQLConfig setKeyMap(Map keyMap);
List getRaw();
SQLConfig setRaw(List raw);
diff --git a/APIJSONORM/src/main/java/apijson/orm/Verifier.java b/APIJSONORM/src/main/java/apijson/orm/Verifier.java
index 366adb07..a4ab7205 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Verifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Verifier.java
@@ -18,7 +18,6 @@ public interface Verifier {
/**验证权限是否通过
* @param config
- * @param visitor
* @return
* @throws Exception
*/
@@ -37,12 +36,10 @@ public interface Verifier {
void verifyRole(SQLConfig config, String table, RequestMethod method, String role) throws Exception;
/**登录校验
- * @param config
* @throws Exception
*/
void verifyLogin() throws Exception;
/**管理员角色校验
- * @param config
* @throws Exception
*/
void verifyAdmin() throws Exception;
@@ -67,24 +64,36 @@ public interface Verifier {
void verifyRepeat(String table, String key, Object value, long exceptId) throws Exception;
/**验证请求参数的数据和结构
- * @param table
- * @param key
- * @param value
- * @param exceptId 不包含id
+ * @param method
+ * @param name
+ * @param target
+ * @param request
+ * @param maxUpdateCount
+ * @param globalDatabase
+ * @param globalSchema
+ * @param creator
+ * @return
* @throws Exception
*/
JSONObject verifyRequest(RequestMethod method, String name, JSONObject target, JSONObject request,
int maxUpdateCount, String globalDatabase, String globalSchema, SQLCreator creator) throws Exception;
/**验证返回结果的数据和结构
- * @param table
- * @param key
- * @param value
- * @param exceptId 不包含id
+ * @param method
+ * @param name
+ * @param target
+ * @param response
+ * @param database
+ * @param schema
+ * @param creator
+ * @param callback
+ * @return
* @throws Exception
*/
- JSONObject verifyResponse(RequestMethod method, String name, JSONObject target, JSONObject response,
- String database, String schema, SQLCreator creator, OnParseCallback callback) throws Exception;
+ JSONObject verifyResponse(
+ RequestMethod method, String name, JSONObject target, JSONObject response,
+ String database, String schema, SQLCreator creator, OnParseCallback callback
+ ) throws Exception;
@NotNull
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ffcbf272..f9c33a7a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,7 +27,7 @@
- [gdjs2](https://github.com/gdjs2)(University of California, Riverside)
- [Rkyzzy](https://github.com/Rkyzzy)(SUSTech, University of California, Berkeley)
- [kxlv2000](https://github.com/kxlv2000)(SUSTech)
-- [caohao-php](https://github.com/caohao-php)(腾讯工程师)
+- [caohao-go](https://github.com/caohao-go)(腾讯工程师,曾在华为、恒生担任C/C++开发工程师,在wps担任项目经理,在360担任技术专家)
- [Wscats](https://github.com/Wscats)(腾讯工程师、腾讯 AlloyTeam 成员、Tencent Creation Camp 成员、知名技术博主)
- [jun0315](https://github.com/jun0315)(腾讯工程师)
- [JieJo](https://github.com/JieJo)
diff --git a/README.md b/README.md
index 76721b69..fc9b199a 100644
--- a/README.md
+++ b/README.md
@@ -24,17 +24,19 @@ This source code is licensed under the Apache License Version 2.0
-
-
-
+
-
+
+
+
+
+
@@ -351,6 +353,7 @@ https://github.com/Tencent/APIJSON/issues/187
* [爱投斯智能技术(深圳)有限公司](http://www.aiotos.net)
* [邻盛科技(武汉)有限公司](http://www.linksame.com)
* [上海麦市信息科技有限公司](https://www.masscms.com)
+ * [上海翊丞互联网科技有限公司](http://www.renrencjl.com/home)
### 贡献者们
主项目 APIJSON 的贡献者们(6 个腾讯工程师、1 个微软工程师、1 个阿里云工程师、1 个字节跳动工程师、1 个网易工程师、1 个 Zoom 工程师、1 个圆通工程师、1 个知乎基础研发架构师、1 个智联招聘工程师、1 个美国加州大学学生、3 个 SUSTech 学生等):
@@ -680,7 +683,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[quick-boot](https://github.com/csx-bill/quick-boot) 基于 Spring Cloud 2022、Spring Boot 3、AMIS 和 APIJSON 的低代码系统。
[apijson-query-spring-boot-starter](https://gitee.com/mingbaobaba/apijson-query-spring-boot-starter) 一个快速构建 APIJSON 查询条件的插件
-
+
+[apijson-builder](https://github.com/yeli19950109/apijson-builder) 简单包装 APIJSON,相比直接构造查询 JSON 更好记,ts 编写,调整了一些参数和使用方式
感谢热心的作者们的贡献,点 ⭐Star 支持下他们吧~