Skip to content

Commit b67eb21

Browse files
committed
Java:MultiDataSource 新增 /execute 接口来支持 SQLAuto
1 parent c6f9640 commit b67eb21

File tree

2 files changed

+152
-45
lines changed

2 files changed

+152
-45
lines changed

APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/boot/DemoController.java

+142-45
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,31 @@
1414

1515
package apijson.boot;
1616

17-
import static apijson.RequestMethod.DELETE;
18-
import static apijson.RequestMethod.GET;
19-
import static apijson.RequestMethod.GETS;
20-
import static apijson.RequestMethod.HEAD;
21-
import static apijson.RequestMethod.HEADS;
22-
import static apijson.RequestMethod.POST;
23-
import static apijson.RequestMethod.PUT;
24-
import static apijson.framework.APIJSONConstant.ACCESS_;
25-
import static apijson.framework.APIJSONConstant.COUNT;
26-
import static apijson.framework.APIJSONConstant.FORMAT;
27-
import static apijson.framework.APIJSONConstant.FUNCTION_;
28-
import static apijson.framework.APIJSONConstant.ID;
29-
import static apijson.framework.APIJSONConstant.REQUEST_;
30-
import static apijson.framework.APIJSONConstant.USER_ID;
31-
import static apijson.framework.APIJSONConstant.VERSION;
32-
import static org.springframework.http.HttpHeaders.COOKIE;
33-
import static org.springframework.http.HttpHeaders.SET_COOKIE;
17+
import com.alibaba.fastjson.JSONArray;
18+
import com.alibaba.fastjson.JSONObject;
19+
import com.fasterxml.jackson.databind.util.LRUMap;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.http.HttpEntity;
23+
import org.springframework.http.HttpHeaders;
24+
import org.springframework.http.HttpMethod;
25+
import org.springframework.http.ResponseEntity;
26+
import org.springframework.stereotype.Service;
27+
import org.springframework.web.bind.annotation.GetMapping;
28+
import org.springframework.web.bind.annotation.PathVariable;
29+
import org.springframework.web.bind.annotation.PostMapping;
30+
import org.springframework.web.bind.annotation.RequestBody;
31+
import org.springframework.web.bind.annotation.RequestMapping;
32+
import org.springframework.web.bind.annotation.RequestParam;
33+
import org.springframework.web.bind.annotation.RestController;
34+
import org.springframework.web.client.RestTemplate;
3435

3536
import java.net.URLDecoder;
3637
import java.rmi.ServerException;
38+
import java.sql.PreparedStatement;
39+
import java.sql.ResultSet;
40+
import java.sql.ResultSetMetaData;
41+
import java.sql.Statement;
3742
import java.util.ArrayList;
3843
import java.util.Arrays;
3944
import java.util.Enumeration;
@@ -49,31 +54,15 @@
4954
import javax.servlet.http.HttpServletResponse;
5055
import javax.servlet.http.HttpSession;
5156

52-
import org.springframework.beans.factory.annotation.Autowired;
53-
import org.springframework.http.HttpEntity;
54-
import org.springframework.http.HttpHeaders;
55-
import org.springframework.http.HttpMethod;
56-
import org.springframework.http.ResponseEntity;
57-
import org.springframework.stereotype.Service;
58-
import org.springframework.web.bind.annotation.GetMapping;
59-
import org.springframework.web.bind.annotation.PathVariable;
60-
import org.springframework.web.bind.annotation.PostMapping;
61-
import org.springframework.web.bind.annotation.RequestBody;
62-
import org.springframework.web.bind.annotation.RequestMapping;
63-
import org.springframework.web.bind.annotation.RequestParam;
64-
import org.springframework.web.bind.annotation.RestController;
65-
import org.springframework.web.client.RestTemplate;
66-
67-
import com.alibaba.fastjson.JSONObject;
68-
import com.fasterxml.jackson.databind.util.LRUMap;
69-
7057
import apijson.JSON;
7158
import apijson.JSONResponse;
7259
import apijson.Log;
7360
import apijson.RequestMethod;
7461
import apijson.StringUtil;
7562
import apijson.demo.DemoFunctionParser;
7663
import apijson.demo.DemoParser;
64+
import apijson.demo.DemoSQLConfig;
65+
import apijson.demo.DemoSQLExecutor;
7766
import apijson.demo.DemoVerifier;
7867
import apijson.demo.model.Privacy;
7968
import apijson.demo.model.User;
@@ -86,11 +75,29 @@
8675
import apijson.orm.exception.OutOfRangeException;
8776
import apijson.router.APIJSONRouterController;
8877

78+
import static apijson.RequestMethod.DELETE;
79+
import static apijson.RequestMethod.GET;
80+
import static apijson.RequestMethod.GETS;
81+
import static apijson.RequestMethod.HEAD;
82+
import static apijson.RequestMethod.HEADS;
83+
import static apijson.RequestMethod.POST;
84+
import static apijson.RequestMethod.PUT;
85+
import static apijson.framework.APIJSONConstant.ACCESS_;
86+
import static apijson.framework.APIJSONConstant.COUNT;
87+
import static apijson.framework.APIJSONConstant.FORMAT;
88+
import static apijson.framework.APIJSONConstant.FUNCTION_;
89+
import static apijson.framework.APIJSONConstant.ID;
90+
import static apijson.framework.APIJSONConstant.REQUEST_;
91+
import static apijson.framework.APIJSONConstant.USER_ID;
92+
import static apijson.framework.APIJSONConstant.VERSION;
93+
import static org.springframework.http.HttpHeaders.COOKIE;
94+
import static org.springframework.http.HttpHeaders.SET_COOKIE;
95+
8996

9097
/**请求路由入口控制器,包括通用增删改查接口等,转交给 APIJSON 的 Parser 来处理
9198
* 具体见 SpringBoot 文档
9299
* https://www.springcloud.cc/spring-boot.html#boot-features-spring-mvc
93-
* 以及 APIJSON 通用文档 3.设计规范 3.1 操作方法
100+
* 以及 APIJSON 通用文档 3.设计规范 3.1 操作方法
94101
* https://github.com/Tencent/APIJSON/blob/master/Document.md#3.1
95102
* <br > 建议全通过HTTP POST来请求:
96103
* <br > 1.减少代码 - 客户端无需写HTTP GET,PUT等各种方式的请求代码
@@ -123,7 +130,7 @@ public String getRequestURL() {
123130
public String router(@PathVariable String method, @PathVariable String tag, @RequestParam Map<String, String> params, @RequestBody String request, HttpSession session) {
124131
return super.router(method, tag, params, request, session);
125132
}
126-
133+
127134
// 通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
128135

129136
/**增删改查统一入口,这个一个方法可替代以下 7 个方法,牺牲一些路由解析性能来提升一点开发效率
@@ -713,7 +720,7 @@ public JSONObject login(@RequestBody String request, HttpSession session) {
713720
return privacyResponse;
714721
}
715722

716-
//校验凭证
723+
//校验凭证
717724
if (isPassword) {//password密码登录
718725
response = new JSONResponse(
719726
new DemoParser(HEADS, false).parseResponse(
@@ -853,7 +860,7 @@ public JSONObject register(@RequestBody String request) {
853860
requestObject.put(JSONRequest.KEY_TAG, REGISTER);
854861
}
855862
requestObject.put(JSONRequest.KEY_FORMAT, true);
856-
response = new JSONResponse(
863+
response = new JSONResponse(
857864
new DemoParser(POST).setNeedVerifyLogin(false).parseResponse(requestObject)
858865
);
859866

@@ -982,7 +989,7 @@ public JSONObject putPassword(@RequestBody String request){
982989
} else {
983990
privacy.setPayPassword(oldPassword);
984991
}
985-
JSONResponse response = new JSONResponse(
992+
JSONResponse response = new JSONResponse(
986993
new DemoParser(HEAD, false).parseResponse(
987994
new JSONRequest(privacy).setFormat(true)
988995
)
@@ -1169,11 +1176,11 @@ public void remove(String key) {
11691176
@SuppressWarnings("unchecked")
11701177
@RequestMapping(value = "delegate")
11711178
public String delegate(
1172-
@RequestParam("$_delegate_url") String url,
1179+
@RequestParam("$_delegate_url") String url,
11731180
@RequestParam(value = "$_type", required = false) String type,
11741181
@RequestParam(value = "$_except_headers", required = false) String exceptHeaders,
1175-
@RequestParam(value = "$_delegate_id", required = false) String sessionId,
1176-
@RequestBody(required = false) String body,
1182+
@RequestParam(value = "$_delegate_id", required = false) String sessionId,
1183+
@RequestBody(required = false) String body,
11771184
HttpMethod method, HttpSession session
11781185
) {
11791186

@@ -1210,7 +1217,7 @@ public String delegate(
12101217
if (names != null) {
12111218
headers = new HttpHeaders();
12121219
//Arrays.asList(null) 抛异常,可以排除不存在的头来替代 exceptHeaders == null //空字符串表示不排除任何头
1213-
List<String> exceptHeaderList = StringUtil.isEmpty(exceptHeaders, true)
1220+
List<String> exceptHeaderList = StringUtil.isEmpty(exceptHeaders, true)
12141221
? EXCEPT_HEADER_LIST : Arrays.asList(StringUtil.split(exceptHeaders));
12151222

12161223

@@ -1221,7 +1228,7 @@ public String delegate(
12211228
while (names.hasMoreElements()) {
12221229
name = names.nextElement();
12231230
if (name != null && exceptHeaderList.contains(name.toLowerCase()) == false) {
1224-
//APIAuto 是一定精准发送 Set-Cookie 名称过来的,预留其它命名可实现覆盖原 Cookie Header 等更多可能
1231+
//APIAuto 是一定精准发送 Set-Cookie 名称过来的,预留其它命名可实现覆盖原 Cookie Header 等更多可能
12251232
if (SET_COOKIE.toLowerCase().equals(name.toLowerCase())) { //接收到时就已经被强制小写
12261233
setCookie = Arrays.asList(httpServletRequest.getHeader(name)); // JSON.parseArray(request.getHeader(name), String.class);
12271234
}
@@ -1317,6 +1324,96 @@ else if (APIJSON_DELEGATE_ID.toLowerCase().equals(name.toLowerCase())) {
13171324
return entity.getBody();
13181325
}
13191326

1327+
/**执行 SQL 语句,支持 SQLAuto,注意仅仅不要开放给后端组外的任何人,更不要暴露到公司外的公网!
1328+
* @param request 只用String,避免encode后未decode
1329+
* @return
1330+
* @see
1331+
* <pre>
1332+
{
1333+
"sql": "SELECT * FROM sys.Access LIMIT ${limit}", // SQL 语句,可以带占位符
1334+
"arg": {
1335+
"limit": 5
1336+
}
1337+
}
1338+
* </pre>
1339+
*/
1340+
@PostMapping("execute")
1341+
public String execute(@RequestBody String request, HttpSession session) {
1342+
try {
1343+
if (Log.DEBUG == false) {
1344+
return DemoParser.newErrorResult(new IllegalAccessException("非 DEBUG 模式下不允许调用 /execute !")).toJSONString();
1345+
}
1346+
1347+
DemoVerifier.verifyLogin(session);
1348+
1349+
long startTime = System.currentTimeMillis();
1350+
1351+
JSONObject req = JSON.parseObject(request);
1352+
String uri = req.getString("uri");
1353+
String sql = req.getString("sql");
1354+
List<Object> valueList = req.getJSONArray("arg");
1355+
1356+
DemoSQLExecutor executor = new DemoSQLExecutor();
1357+
DemoSQLConfig config = new DemoSQLConfig();
1358+
1359+
if (StringUtil.isNotEmpty(uri)) {
1360+
config.setDBUri(uri);
1361+
}
1362+
config.setPrepared(true);
1363+
config.setPreparedValueList(valueList);
1364+
1365+
Statement statement = executor.getStatement(config, sql);
1366+
if (statement instanceof PreparedStatement) {
1367+
((PreparedStatement) statement).execute();
1368+
} else {
1369+
statement.execute(sql);
1370+
}
1371+
1372+
ResultSet rs = statement.getResultSet();
1373+
ResultSetMetaData rsmd = rs.getMetaData();
1374+
int length = rsmd.getColumnCount();
1375+
1376+
JSONArray arr = new JSONArray();
1377+
1378+
long cursorDuration = 0;
1379+
long rsDuration = 0;
1380+
1381+
long cursorStartTime = System.currentTimeMillis();
1382+
while (rs.next()) {
1383+
cursorDuration += System.currentTimeMillis() - cursorStartTime;
1384+
1385+
JSONObject obj = new JSONObject(true);
1386+
for (int i = 1; i <= length; i++) {
1387+
long sqlStartTime = System.currentTimeMillis();
1388+
String label = rsmd.getColumnLabel(i);
1389+
Object value = rs.getObject(i);
1390+
rsDuration += System.currentTimeMillis() - sqlStartTime;
1391+
1392+
obj.put(label, value);
1393+
}
1394+
1395+
arr.add(obj);
1396+
}
1397+
1398+
JSONObject result = DemoParser.newSuccessResult();
1399+
result.put("count", statement.getUpdateCount());
1400+
result.put("list", arr);
1401+
1402+
long endTime = System.currentTimeMillis();
1403+
long duration = endTime - startTime;
1404+
1405+
long sqlDuration = cursorDuration + rsDuration;
1406+
long parseDuration = duration - sqlDuration;
1407+
1408+
result.put("time:start|duration|end|parse|sql", startTime + "|" + duration + "|" + endTime + "|" + parseDuration + "|" + sqlDuration);
1409+
1410+
return result.toJSONString();
1411+
} catch (Exception e) {
1412+
return DemoParser.newErrorResult(e).toJSONString();
1413+
}
1414+
1415+
}
1416+
13201417

13211418
/**Swagger 文档 Demo,供 APIAuto 测试导入 Swagger 文档到数据库用
13221419
* @return

APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/demo/DemoSQLConfig.java

+10
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,17 @@ public String getDBVersion() {
163163
return null;
164164
}
165165

166+
private String dbUri;
167+
public void setDBUri(String dbUri) {
168+
this.dbUri = dbUri;
169+
}
166170
@JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加
167171
@Override
168172
public String getDBUri() {
173+
if (StringUtil.isNotEmpty(dbUri)) {
174+
return dbUri;
175+
}
176+
169177
if (isMySQL()) {
170178
// 这个是 MySQL 8.0 及以上,要加 userSSL=false return "jdbc:mysql://localhost:3306?userSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8";
171179
// 以下是 MySQL 5.7 及以下
@@ -322,4 +330,6 @@ protected void onJoinComplextRelation(String sql, String quote, Join j, String j
322330
// 开启 JOIN ON t1.c1 LIKE concat('%', t2.c2, '%') 等复杂关联 super.onJoinComplextRelation(sql, quote, j, jt, onList, on);
323331
}
324332

333+
334+
325335
}

0 commit comments

Comments
 (0)