From 3d210639c6d20b94679cd1268b549b64fa35288a Mon Sep 17 00:00:00 2001 From: iceewei Date: Mon, 5 Apr 2021 22:27:10 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=A0=81=E5=8F=AA?= =?UTF-8?q?=E5=9C=A8=E6=9C=80=E5=A4=96=E5=B1=82=E8=BF=94=E5=9B=9E=EF=BC=8C?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/apijson/orm/AbstractSQLExecutor.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java index a3ff117cf..324cd6350 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java @@ -210,8 +210,11 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws int updateCount = executeUpdate(config); - result = AbstractParser.newResult(updateCount > 0 ? JSONResponse.CODE_SUCCESS : JSONResponse.CODE_NOT_FOUND - , updateCount > 0 ? JSONResponse.MSG_SUCCEED : "没权限访问或对象不存在!"); + result = new JSONObject(); + if (config.getMethod() == RequestMethod.DELETE) { + result = AbstractParser.newResult(updateCount > 0 ? JSONResponse.CODE_SUCCESS : JSONResponse.CODE_NOT_FOUND, + updateCount > 0 ? JSONResponse.MSG_SUCCEED : "没权限访问或对象不存在!"); + } //id,id{}至少一个会有,一定会返回,不用抛异常来阻止关联写操作时前面错误导致后面无条件执行! result.put(JSONResponse.KEY_COUNT, updateCount);//返回修改的记录数 @@ -717,8 +720,8 @@ public Connection getConnection(@NotNull SQLConfig config) throws Exception { connection = connectionMap.get(config.getDatabase()); if (connection == null || connection.isClosed()) { Log.i(TAG, "select connection " + (connection == null ? " = null" : ("isClosed = " + connection.isClosed()))) ; - // PostgreSQL 不允许 cross-database - connection = DriverManager.getConnection(config.getDBUri(), config.getDBAccount(), config.getDBPassword()); + // PostgreSQL 不允许 cross-database + connection = DriverManager.getConnection(config.getDBUri(), config.getDBAccount(), config.getDBPassword()); connectionMap.put(config.getDatabase(), connection); } @@ -823,14 +826,14 @@ public ResultSet executeQuery(@NotNull SQLConfig config) throws Exception { public int executeUpdate(@NotNull SQLConfig config) throws Exception { PreparedStatement s = getStatement(config); int count = s.executeUpdate(); //PreparedStatement 不用传 SQL - + if (config.getMethod() == RequestMethod.POST && config.getId() == null) { //自增id ResultSet rs = s.getGeneratedKeys(); if (rs != null && rs.next()) { config.setId(rs.getLong(1));//返回插入的主键id } } - + return count; } From 737aa738efb8fb5ca362b537c8adc49b9d225f9c Mon Sep 17 00:00:00 2001 From: iceewei Date: Mon, 5 Apr 2021 22:29:18 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=A0=81=E5=8F=AA?= =?UTF-8?q?=E5=9C=A8=E6=9C=80=E5=A4=96=E5=B1=82=E8=BF=94=E5=9B=9E=EF=BC=8C?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E7=BB=9F=E4=B8=80?= 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 insertion(+) diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java index 324cd6350..043251a56 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java @@ -212,6 +212,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws result = new JSONObject(); if (config.getMethod() == RequestMethod.DELETE) { + // 特别地,针对DELETE请求,如果需要提示code,可以用内部的code来判断。其余请求类型统一使用外层错误码。 result = AbstractParser.newResult(updateCount > 0 ? JSONResponse.CODE_SUCCESS : JSONResponse.CODE_NOT_FOUND, updateCount > 0 ? JSONResponse.MSG_SUCCEED : "没权限访问或对象不存在!"); } From 635d2413683b3e1eecb18a7af50cde7fb42be681 Mon Sep 17 00:00:00 2001 From: iceewei Date: Tue, 6 Apr 2021 15:37:16 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E4=BF=A1=E6=81=AF=E6=8E=A7=E5=88=B6=E9=9D=99=E6=80=81?= =?UTF-8?q?=E5=8F=98=E9=87=8F=EF=BC=8C=E6=94=B9=E9=80=A0=E4=B8=A4=E5=B1=82?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=A0=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AbstractParser类增加了isPrintErrorLog静态变量,暴露给用户控制错误信息抛出 - 内外两层错误码改造,增加抛出异常逻辑(updateCount <= 0) --- .../src/main/java/apijson/orm/AbstractParser.java | 8 ++++++-- .../main/java/apijson/orm/AbstractSQLExecutor.java | 12 ++++++------ Document.md | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java index a22c05d5c..be8ef09e3 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java @@ -301,6 +301,9 @@ public JSONObject parseResponse(String request) { private int queryDepth; + // 打印异常日志的标识。线上环境比较敏感,可以通过切换该变量来控制异常栈抛出、错误日志打印。保守起见,该值默认为false。 + public static boolean isPrintErrorLog = false; + /**解析请求json并获取对应结果 * @param request * @return requestObject @@ -383,11 +386,12 @@ public JSONObject parseResponse(JSONObject request) { long endTime = System.currentTimeMillis(); long duration = endTime - startTime; - if (Log.DEBUG) { //用 | 替代 /,避免 APIJSON ORM,APIAuto 等解析路径错误 + if (isPrintErrorLog) { //用 | 替代 /,避免 APIJSON ORM,APIAuto 等解析路径错误 requestObject.put("sql:generate|cache|execute|maxExecute", getSQLExecutor().getGeneratedSQLCount() + "|" + getSQLExecutor().getCachedSQLCount() + "|" + getSQLExecutor().getExecutedSQLCount() + "|" + getMaxSQLCount()); requestObject.put("depth:count|max", queryDepth + "|" + getMaxQueryDepth()); requestObject.put("time:start|duration|end", startTime + "|" + duration + "|" + endTime); if (error != null) { + Log.d(TAG, String.format("onObjectParse error, error is %s", error.getMessage())); requestObject.put("throw", error.getClass().getName()); requestObject.put("trace", error.getStackTrace()); } @@ -397,7 +401,7 @@ public JSONObject parseResponse(JSONObject request) { //会不会导致原来的session = null? session = null; - if (Log.DEBUG) { + if (isPrintErrorLog) { Log.d(TAG, "\n\n\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n " + requestMethod + "/parseResponse request = \n" + requestString + "\n\n"); diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java index 043251a56..85b4162dc 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java @@ -27,6 +27,7 @@ import java.util.Map.Entry; import java.util.Set; +import apijson.orm.exception.NotExistException; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -209,14 +210,13 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws executedSQLCount ++; int updateCount = executeUpdate(config); - - result = new JSONObject(); - if (config.getMethod() == RequestMethod.DELETE) { - // 特别地,针对DELETE请求,如果需要提示code,可以用内部的code来判断。其余请求类型统一使用外层错误码。 - result = AbstractParser.newResult(updateCount > 0 ? JSONResponse.CODE_SUCCESS : JSONResponse.CODE_NOT_FOUND, - updateCount > 0 ? JSONResponse.MSG_SUCCEED : "没权限访问或对象不存在!"); + if (updateCount <= 0) { + throw new NotExistException("没权限访问或对象不存在!"); } + // 更新成功后收集结果。例如更新操作成功时,返回count(affected rows)、id字段 + result = new JSONObject(true); + //id,id{}至少一个会有,一定会返回,不用抛异常来阻止关联写操作时前面错误导致后面无条件执行! result.put(JSONResponse.KEY_COUNT, updateCount);//返回修改的记录数 if (config.getId() != null) { diff --git a/Document.md b/Document.md index 701932c9b..c7e7be53c 100644 --- a/Document.md +++ b/Document.md @@ -318,7 +318,7 @@ 3.请求中的 / 需要转义。JSONRequest.java已经用URLEncoder.encode转义,不需要再写;但如果是浏览器或Postman等直接输入url/request,需要把request中的所有 / 都改成 %252F 。下同。
4.code,指返回结果中的状态码,200表示成功,其它都是错误码,值全部都是HTTP标准状态码。下同。
5.msg,指返回结果中的状态信息,对成功结果或错误原因的详细说明。下同。
-6.code和msg总是在返回结果的同一层级成对出现。对所有请求的返回结果都会在最外层有一对总结式code和msg。对非GET类型的请求,返回结果里面的每个JSONObject里都会有一对code和msg说明这个JSONObject的状态。下同。
+6.code和msg总是在返回结果的同一层级成对出现。对所有请求的返回结果都会在最外层有一对总结式code和msg。下同。
7.id等字段对应的值仅供说明,不一定是数据库里存在的,请求里用的是真实存在的值。下同。
@@ -334,7 +334,7 @@ GET:
普通获取数据,
可用浏览器调试 | base_url/get/ | {< HEAD:
普通获取数量,
可用浏览器调试 | base_url/head/ | {
   TableName:{
     …
   }
}
{…}内为限制条件

例如获取一个 id = 38710 的 User 所发布的 Moment 总数:
{
   "Moment":{
     "userId":38710
   }
}
后端校验通过后自动解析为 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,其它同GET | 同GET HEADS:
安全/私密获取数量,
用于获取银行卡数量等
对安全性要求高的数据总数 | base_url/heads/ | 最外层加一个 "tag":tag,其它同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"
}
后端校验通过后自动解析为 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:[]"
}
后端校验通过后自动解析为 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"
} +POST:
新增数据 | base_url/post/ | 单个:
{
   TableName:{
     …
   },
   "tag":tag
}
{…}中id由后端生成,不能传

例如当前登录用户 38710 发布一个新 Comment:
{
   "Comment":{
     "momentId":12,
     "content":"APIJSON,let interfaces and documents go to hell !"
   },
   "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:[]"
}
后端校验通过后自动解析为 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"
}
后端校验通过后自动解析为 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 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[]"
}
后端校验通过后自动解析为 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"
} From 873afa8470d189648e5e5dc30fc0827da01746c5 Mon Sep 17 00:00:00 2001 From: iceewei Date: Tue, 6 Apr 2021 16:51:57 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E6=8E=A7=E5=88=B6=E9=9D=99=E6=80=81=E5=8F=98?= =?UTF-8?q?=E9=87=8F=EF=BC=8C=E6=94=B9=E9=80=A0=E4=B8=A4=E5=B1=82=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=A0=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AbstractParser类增加了isPrintErrorLog静态变量,暴露给用户控制错误信息抛出 - 内外两层错误码改造,增加抛出异常逻辑(updateCount <= 0) --- APIJSONORM/pom.xml | 2 +- APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml index b892374e2..4c1041bc9 100755 --- a/APIJSONORM/pom.xml +++ b/APIJSONORM/pom.xml @@ -5,7 +5,7 @@ apijson.orm apijson-orm - 4.6.6 + 4.6.7 jar APIJSONORM diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java index 85b4162dc..8966d0132 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java @@ -214,7 +214,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws throw new NotExistException("没权限访问或对象不存在!"); } - // 更新成功后收集结果。例如更新操作成功时,返回count(affected rows)、id字段 + // updateCount>0时收集结果。例如更新操作成功时,返回count(affected rows)、id字段 result = new JSONObject(true); //id,id{}至少一个会有,一定会返回,不用抛异常来阻止关联写操作时前面错误导致后面无条件执行!