Tencent/APIJSON

crud复杂语句catch删除脏数据,请问有好的解决方案吗?

cloudAndMonkey opened this issue · 23 comments

Description

try {
	crud(db,redis等)
} catch(){
	//TODO 删除redis key
} finally{
	
}

请问实现上面的效果,有好的解决方案吗?
前置/后置函数,加一个异常执行函数?

可以通过 @Try + @catch + @finally 实现

@try: true,
@catch: "catchFun(id)" // 对应调用 DemoFunctionParser 中的 public Object catchFun(JSONObject curObj, Throwable e, String idKey)
@finally: "delRedisKey('User-82001')" // 对应调用 DemoFunctionParser 中的 public void delRedisKey(JSONObject curObj, String key) // String k = getArgVal(key) 自动根据单引号判断为值,根据 User/id 判断为路径来按路径取值等

目前仅有 @Try 已实现,其它两个你试试
https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/JSONObject.java#L132-L136

image

可以通过 @Try + @catch + @finally 实现

@try: true,
@catch: "catchFun(id)" // 对应调用 DemoFunctionParser 中的 public Object catchFun(JSONObject curObj, Throwable e, String idKey)
@finally: "delRedisKey('User-82001')" // 对应调用 DemoFunctionParser 中的 public void delRedisKey(JSONObject curObj, String key) // String k = getArgVal(key) 自动根据单引号判断为值,根据 User/id 判断为路径来按路径取值等

目前仅有 @Try 已实现,其它两个你试试 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/JSONObject.java#L132-L136

image

其他两个没有实现,哈哈

@TommyLemon

} catch (Exception e) {
	if (tri == false) {
		if(StringUtil.isNotEmpty(isCatch)) {
			String catchKey = JSONRequest.KEY_CATCH.substring(1);
			onParse(catchKey + "()", isCatch);
			parseFunction(catchKey + "()", catchKey, isCatch, parentPath, this.name, request, false);
		}
		throw CommonException.wrap(e, sqlConfig);  // 不忽略错误,抛异常
	}
	invalidate();  // 忽略错误,还原request
} 

onParse, parseFunction 两种方式调用执行流程是一样的,你认为那种方式合适呢?

parseFunction 更简单直接,应该放到 if (tri == false) {} 后面,只在 try 时生效。
或者只要 @catch/@finally 传了有效值,不需要传 @Try: true 就生效?这样使用简单一些

@finally 需要在 catch (Exception e) 和 正常走完流程后都执行,最好找一个同时满足两者的地方,写一处代码就行,实在不行就只能分开写两处了

只要内部抛了异常,肯定不能拿到正常流程的结果,只能拿到 catch 异常后的结果,可以考虑 @default 返回默认值

parseFunction 更简单直接,应该放到 if (tri == false) {} 后面,只在 try 时生效。 或者只要 @catch/@finally 传了有效值,不需要传 @Try: true 就生效?这样使用简单一些

不需要传 @Try: true 就生效,这样简单.

} catch (Exception e) {
	if (tri == false) {
		throw CommonException.wrap(e, sqlConfig);  // 不忽略错误,抛异常
	}
	if(StringUtil.isNotEmpty(isCatch)) {
		String catchKey = JSONRequest.KEY_CATCH.substring(1);
		parseFunction(catchKey + "()", catchKey, isCatch, parentPath, this.name, request, false);
	}
	invalidate();  // 忽略错误,还原request
}

@TommyLemon
场景: 查询数据,先从缓存查询,缓存存在直接返回.缓存不存在查询数据库.
可以控制某一步直接返回
请问有推荐的解决方案吗?
比如申明 "@block":true
image

@catch: "fun(args)" 调用的函数应该也支持返回值,也就是最后再返回
"@catch": 1

"@catch": {
"id": 1,
"content": "abc"
}

这样的任意值,除非调用的远程函数返回类型是 void 或 return null 才不返回,这样功能更完善一些。
@finally 也一样。

加 Redis 等内存缓存,直接在 DemoSQLExecutor 重写 putCache, getCache, removeCache 就行了,没必要前端新增传参
https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/demo/DemoSQLExecutor.java#L68-L130
image

控制某一步直接返回的目的是啥?

@catch: "fun(args)" 调用的函数应该也支持返回值,也就是最后再返回 "@catch": 1

"@catch": { "id": 1, "content": "abc" }

这样的任意值,除非调用的远程函数返回类型是 void 或 return null 才不返回,这样功能更完善一些。 @finally 也一样。

收到

@TommyLemon
分页支持从0, 1开始,添加一个开关即可,请问需要支持吗?
https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L1295

if(AbstractParser.DEFAULT_QUERY_PAGE == false) {
   page2 = page2 > 0 ? page2 -1 : page2;
}

这个需要的,DEFAULT_QUERY_PAGE 要改成更直观的名字,例如 IS_PAGE_START_FROM_ONE 这种

@TommyLemon 最近有点忙,不好意思
@catch 返回值已经支持
image
问题:
1、catch 是否放在 try 前面 ?

} catch (Exception e) {
	if(isCatch != null) {
		processCallFunction(KEY_CATCH, isCatch);
	}
	if (tri == false) {
		throw CommonException.wrap(e, sqlConfig);  // 不忽略错误,抛异常
	}
	invalidate();  // 忽略错误,还原request
} finally {
	if(isFinally != null) {
		processCallFunction(KEY_FINALLY, isFinally);
	}
}

public void processCallFunction(String key, String value) throws Exception {
    String funKey = key.substring(1);
    parseFunction(funKey + "()", funKey, value, parentPath, this.name, request, false);
    if(response.get(funKey) != null) {
	    this.parser.requestObject.put(key, response.get(funKey));  // 设置返回值
    }
}

2、@finally 执行逻辑和 @catch保持一致吗?
3、分页变量名已经变更为: IS_PAGE_START_FROM_ONE
image

赞。

tri 已经不需要了,JSONObject.KEY_TRY 可以标记 @deprecated,以上代码对应改为

        if (catch_ == null) { // isCatch 看起来是 Boolean 类型,其实是 String 类型,前端传 @catch:"" 表示仅不抛异常,但不执行远程函数
           throw CommonException.wrap(e, sqlConfig);  // 不忽略错误,抛异常
       }
       
       if (StringUtil.isNotEmpty(catch_)) { // 这个判断移到 processCallFunction 更好,和 @finally 统一处理
           processCallFunction(KEY_CATCH,  catch_);
       }
       invalidate();  // 忽略错误,还原 request

同理 isFinally 命名改为 finally_ 或 finallyVal 之类的更好

@TommyLemon
代码已经调整,功能测试通过. 你再看看代码是否还有需要优化的点?

} catch (Exception e) {
	if (catch_ == null) { // 前端传 @catch:"" 表示仅不抛异常,但不执行远程函数
	     throw CommonException.wrap(e, sqlConfig);  // 不忽略错误,抛异常
	}
	processCallFunction(KEY_CATCH, catch_);
	invalidate();  // 忽略错误,还原request
} finally {
	processCallFunction(KEY_FINALLY, finally_);
}

public void processCallFunction(String key, String value) throws Exception {
    if(StringUtil.isNotEmpty(value)) {
	    String funKey = key.substring(1);
	    parseFunction(funKey + "()", funKey, value, parentPath, this.name, request, false);
    }
}

返回结果统一处理: https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L538

String warn = Log.DEBUG == false || error != null ? null : getWarnString();
processRequestObject(request, KEY_CATCH);
processRequestObject(request, KEY_FINALLY);
requestObject = error == null ? extendSuccessResult(requestObject, warn, isRoot) : extendErrorResult(requestObject, error, requestMethod, getRequestURL(), isRoot);

public void processRequestObject(JSONObject request, String key) {
  if(request.containsKey(key)) {
	  String _key = key.substring(1);
	  requestObject = requestObject == null ? new JSONObject() : requestObject;
	  requestObject.put(_key, this.queryResultMap.get(_key));
  }
}

测试脚本

{
    "name": "jerry",
    "@catch": "",
    //"@catch": "sayHello(name)",
    "@finally": "sayHello(name)",
	"Document_copy2:data[]": {
        "Document_copy2": {
            "@column" : "id1"
        },
        "page": 0,
    	"count": 10
    },
    "@explain": true,
    "format": true
}


{
    "msg": "success",
    "code": 200,
    "finally": "Hello, jerry",
    "debug:info|help": " \n提 bug 请发请求和响应的【完整截屏】,没图的自行解决! \n开发者有限的时间和精力主要放在【维护项目源码和文档】上! \n【描述不详细】 或 【文档/常见问题 已有答案】 的问题可能会被忽略!! \n【态度 不文明/不友善】的可能会被踢出群,问题也可能不予解答!!! \n\n **环境信息**  \n系统: Mac OS X 12.4 \n数据库: DEFAULT_DATABASE = MYSQL \nJDK: 1.8.0_351 x86_64 \nAPIJSON: 6.3.0 \n   \n【常见问题】:https://github.com/Tencent/APIJSON/issues/36 \n【通用文档】:https://github.com/Tencent/APIJSON/blob/master/Document.md \n【视频教程】:https://search.bilibili.com/all?keyword=APIJSON",
    "ok": true,
    "time": 1705460225810,
    "sql:generate|cache|execute|maxExecute": "1|0|1|200",
    "depth:count|max": "3|5",
    "time:start|duration|end|parse|sql": "1705460225749|61|1705460225810|61|0"
}

{
    "name": "jerry",
    //"@catch": "",
    "@catch": "sayHello(name)",
    "@finally": "sayHello(name)",
    "User_logback[]": [
        {
            "id": "0430f00c-a895-4f25-b096-40382bd85a11",
            "name": "角色"
        },
        {
            "id": "0430f00c-a895-4f25-b096-40382bd85a12",
            "name": "角色3"
        }
    ],
    "tag": "User_logback[]",
    "@explain": true,
    "format": true
}

{
    "msg": "success",
    "code": 200,
    "finally": "Hello, jerry",
    "catch": "Hello, jerry",
    "debug:info|help": " \n提 bug 请发请求和响应的【完整截屏】,没图的自行解决! \n开发者有限的时间和精力主要放在【维护项目源码和文档】上! \n【描述不详细】 或 【文档/常见问题 已有答案】 的问题可能会被忽略!! \n【态度 不文明/不友善】的可能会被踢出群,问题也可能不予解答!!! \n\n **环境信息**  \n系统: Mac OS X 12.4 \n数据库: DEFAULT_DATABASE = MYSQL \nJDK: 1.8.0_351 x86_64 \nAPIJSON: 6.3.0 \n   \n【常见问题】:https://github.com/Tencent/APIJSON/issues/36 \n【通用文档】:https://github.com/Tencent/APIJSON/blob/master/Document.md \n【视频教程】:https://search.bilibili.com/all?keyword=APIJSON",
    "ok": true,
    "time": 1705460325505,
    "sql:generate|cache|execute|maxExecute": "2|0|2|200",
    "depth:count|max": "2|5",
    "time:start|duration|end|parse|sql": "1705460325418|87|1705460325505|57|30"
}

赞,关于返回结果统一处理,可以考虑移到 AbstractObjectParser 单独处理,因为这样 @catch@finally 才能支持在多个不同对象中使用。

AbstractObjectParser.parseFunction 是有 response.put(key, returnVal) 的,
https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java#L1015C14-L1055

应该 setSQLConfig, executeSQL, onFunctionResponse, onChildResponse, onComplete 最前面加上

if (isInvalidate()) {
    return this; // 或对 void 方法 return;
} 

就会自动写入最终结果了

目前看起来直接在 AbstractParser.onObjectParse 对 op 执行方法整体 try-catch-finally 实现简单很多
https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L1125-L1131

只是要想想一个对象本身解析抛异常后,是否仍然继续解析它的内部子对象、剩下未执行的关键词、远程函数等?看起来没必要也不应该,避免继续错下去。
其实我当时想加 @Try 的原因是:
有时需要同时查多个对象,它们之前没有依赖关系,有的甚至是次要的信息,没查到也没关系,希望报错不影响核心对象查询。

Description

try {
	crud(db,redis等)
} catch(){
	//TODO 删除redis key
} finally{
	
}

请问实现上面的效果,有好的解决方案吗? 前置/后置函数,加一个异常执行函数?

如果是你的这个需求,那就需要在 @catch@finally 解析过程或之后继续抛异常。
决定权可以给 catchFun 和 delKey 这两个函数,内部自己决定是否抛异常;
也可以把 @Try 利用上,true 则不自动抛异常,false 则继续抛原来的异常,@catch@finally 仅仅作为不同流程点的拦截处理,似乎这样更好些

@cloudAndMonkey 可以先发 PR 提交代码,我再看看怎么调整下