From 6796698143b56f9cdf3ba1d6ade1707d92b1a2c7 Mon Sep 17 00:00:00 2001 From: Sea Date: Wed, 16 Jan 2019 18:31:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Djoin=E7=9A=84=E9=83=A8?= =?UTF-8?q?=E5=88=86BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 59 +++++++++++++++++- .../biao/apijson/parser/APIJSONProvider.java | 41 +++++++++++- .../java/zuo/biao/apijson/parser/App.java | 18 ++++-- .../APIJSONParser/pom.properties | 2 +- .../biao/apijson/parser/APIJSONProvider.class | Bin 21195 -> 22286 bytes .../classes/zuo/biao/apijson/parser/App.class | Bin 2083 -> 2255 bytes 6 files changed, 109 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1348e2b..90005e2 100644 --- a/README.md +++ b/README.md @@ -314,7 +314,7 @@ WHERE ( regexp_like(p.code,'^[0-9]+$')) 结果: -```json +```sql SELECT p.id, p.code, p.name, p.price FROM Product p WHERE (p.name LIKE '%双11%') @@ -350,21 +350,74 @@ GROUP BY p.name 目前允许的函数没有进行控制,所有的单一参数的函数都可以使用。 +单表查询的内容基本介绍完了,接下来是多表查询。 +现在有两张表,零售表`m_retail`,零售的店铺`m_retail.c_store_id`外键关联到`c_store`表。 +第一种是使用`id@` +```json +{ + "m_retail:r":{ + "@column":"id,docno", + "id": 18 + }, + "c_store:s":{ + "id@":"/r/c_store_id", + "@column":"code,name" + } +} +``` -单表查询的内容基本介绍完了。 +结果: -接下来是多表查询,后续文档内容敬请期待。 +```sql +SELECT r.id, r.docno, s.code as "s.code", s.name as "s.name" +FROM m_retail r, c_store s +WHERE (r.id = 18 AND r.c_store_id = s.id) +``` +如果不使用`id@` +```json +{ + "[]": { + "c_store": { + "@column": "code,name", + "code": "C86L" + }, + "m_retail:r": { + "@column": "id,docno" + } + }, + "join": { + "@innerJoin": [ + "c_store.id=r.c_store_id" + ] + } +} +``` + +结果: +```sql +SELECT r.id, r.docno, c_store.code, c_store.name +FROM m_retail r +INNER JOIN c_store ON c_store.id=r.c_store_id +WHERE (c_store.code = 'C86L') +``` +其他几种join的使用方法和这里演示的innerJoin是一样的。 +现在支持的join有: +@innerJoin +@leftOuterJoin +@rightOuterJoin +@join +@outerJoin diff --git a/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java b/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java index 83ed32e..dd55655 100644 --- a/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java +++ b/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java @@ -12,6 +12,8 @@ public class APIJSONProvider extends AbstractProvider { private final String ALIAS_SPLIT = ":"; private static final String AND = " AND "; private static final String OR = " OR "; + private final String q = "\""; + private boolean isWrap = false; private List tableWhiteList = new ArrayList(); private List tableBlackList = new ArrayList(); @@ -61,7 +63,7 @@ public List getTables() { Set tableNames = request.keySet(); String tableAliasName; String tableRealName; - for(String tableName : tableNames) { + out:for(String tableName : tableNames) { if(!tableName.matches("(\\w+(:\\w+)?)")) { error("表" + tableName + "格式不符合" ); return null; @@ -74,6 +76,19 @@ public List getTables() { }else { tableRealName = tableAliasName = tableName; } + if(join != null) { + String[] checkJoin = {"@innerJoin","@leftOuterJoin","@rightOuterJoin","@join","@outerJoin"}; + for(String joinKey : checkJoin) { + JSONArray array = join.getJSONArray(joinKey); + if(array != null && array.size() != 0) { + for(Object obj : array) { + if(obj instanceof String && (((String)obj).startsWith(tableRealName) || ((String)obj).startsWith(tableAliasName))) { + continue out; + } + } + } + } + } validateTable(tableRealName); list.add(tableRealName + " " + tableAliasName); } @@ -151,18 +166,27 @@ public List getSelect() { String columnRealName = functionStrs[1]; String columnAliasName = columnName.split(ALIAS_SPLIT)[1]; validateColumn(tableRealName,columnRealName); + if(propertis.getString("id@") != null) + columnAliasName = wrapColumn(tableAliasName, columnAliasName); list.add(funcitonName + "(" + tableAliasName + "." + columnRealName + ")" + " as " + columnAliasName); }else { //没函数的字段 String columnRealName = functionOrColumn ; String columnAliasName = columnName.split(ALIAS_SPLIT)[1]; validateColumn(tableRealName,columnRealName); + if(propertis.getString("id@") != null) + columnAliasName = wrapColumn(tableAliasName, columnAliasName); list.add(tableAliasName + "." + columnRealName + " as " + columnAliasName); } }else { //使用tableAliasName.columnName类型 validateColumn(tableRealName,columnName); - list.add(tableAliasName + "." + columnName); + if(propertis.getString("id@") != null) { + String columnAliasName = wrapColumn(tableAliasName, columnName); + list.add(tableAliasName + "." + columnName + " as " + columnAliasName); + }else { + list.add(tableAliasName + "." + columnName); + } } } } @@ -171,6 +195,9 @@ public List getSelect() { } return list; } + private String wrapColumn(String tableAliasName, String columnAliasName) { + return isWrap? q + tableAliasName + "." + columnAliasName + q : q + columnAliasName + q; + } /** * 解析请求中的过滤条件 * 支持以下模式: @@ -520,7 +547,7 @@ public List getOuterJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@outerJoin")!=null) { - JSONArray stms = join.getJSONArray("@join"); + JSONArray stms = join.getJSONArray("@outerJoin"); for(int i = 0; i < stms.size(); i++) { Object obj = stms.get(i); @@ -872,4 +899,12 @@ public List getColumnBlackList() { public void setColumnBlackList(List columnBlackList) { this.columnBlackList = columnBlackList; } + public boolean isWrap() { + return isWrap; + } + public void setWrap(boolean isWrap) { + this.isWrap = isWrap; + } + + } diff --git a/src/main/java/zuo/biao/apijson/parser/App.java b/src/main/java/zuo/biao/apijson/parser/App.java index 06c74db..c45c8e7 100644 --- a/src/main/java/zuo/biao/apijson/parser/App.java +++ b/src/main/java/zuo/biao/apijson/parser/App.java @@ -14,12 +14,22 @@ public static void main( String[] args ) throws SQLProviderException { long st = System.currentTimeMillis(); String json = "{\r\n" + - " \"Product:p\":{\r\n" + - " \"@column\":\"name,max(price):max_price\",\r\n" + - " \"name~\": \"双11\",\r\n" + - " \"@group\": \"name\"\r\n" + + " \"[]\": {\r\n" + + " \"c_store\": {\r\n" + + " \"@column\": \"code,name\",\r\n" + + " \"code\": \"C86L\"\r\n" + + " },\r\n" + + " \"m_retail:r\": {\r\n" + + " \"@column\": \"id,docno\"\r\n" + + " }\r\n" + + " },\r\n" + + " \"join\": {\r\n" + + " \"@innerJoin\": [\r\n" + + " \"c_store.id=r.c_store_id\"\r\n" + + " ]\r\n" + " }\r\n" + "}"; + System.out.println(json); JSONObject req = JSONObject.parseObject(json); long fastJsonet = System.currentTimeMillis(); //JSON解析太耗时了174ms diff --git a/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties b/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties index b28d1ab..646310f 100644 --- a/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties +++ b/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties @@ -1,5 +1,5 @@ #Generated by Maven Integration for Eclipse -#Tue Dec 25 10:09:51 CST 2018 +#Wed Jan 16 11:59:18 CST 2019 version=0.0.1-SNAPSHOT groupId=APIJSONParser m2e.projectName=APIJSONParser diff --git a/target/classes/zuo/biao/apijson/parser/APIJSONProvider.class b/target/classes/zuo/biao/apijson/parser/APIJSONProvider.class index 63452963f6b250840ae48242a48c74604dcf9828..417bd28b848d99338ef4fdd38eb4985dfc4c1913 100644 GIT binary patch literal 22286 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�c6!z8Jxfj?XFf z|42GS9N}bA1j!4W^q+synsCYFjR1LOe@c0KF;d>ZVeJmu`(g6N+gH(^pu91@1vNiG z4*12mO@ljT`UN#yCR*w;8|U$b8Tx6ysP{Wo-l1PsTPbLg*Va5C zDA)A9Q5)0 zo(!Ds7dlrvT2upAmA9n~WSv~DpTa>{dSG zIVPJFi;JKP!R&xocmiTE&JYVe#}Erv0%Gx3T`caUJ<^KNLbV-Xm#a9iF{~KThan&v ze~CR|V&nlp*f|OjBxKUhkq`xLO@Bn4#WnaxV1ih%RlJr#k%ROMv}j;l95b}$U!Y~vBnCgyUkAHbI?9F$f7n#_9Tm`7j-iArhcX*8e&SO$)*!{WUAF45B z(qdCzsxu*?kMs)D0D8$ZNP=g8wjMfourd-TI6|fu`5!PI6r86g@h8%ZSd5cOEhQrO z1!NT$JvF;P+)mf_!4}GD9`-ZewXc&MOhUq0C+&Ux0#~>wB;`xBwE?aQY z*#BNZZz;QJmU!p`Ow-c(07+p3SezUK`e+5q^jm5tiYnk((q= zaPgfUCL-|DOMG#nP!4{rqR=!!uAMlwLIr*8m_I}OR{yMq56dr%#v+fvELp{8LA`<> z0mXBgJ(rskKh9ivFFjA8uNu3&%)Xz^&YEmSr zmx0vFTOn2ZBrBBE4mzn{oRrj*NK&tiO6r#v&$Jk-G2X?wkKh0N&$;)m`@bIel;SE}__@y= zqM>H8sutSa)DH@|C#xmP`17<*5{1&z)8!%&wNTQHSTVnBsIi8RBXCBYIEvZH@szs9 z%1t_TxstPt)x5c+S^qC%>M^}kD3+Ry8lJ!X)WwqNbkux?Df#-&$-@+ zWyDQ=#)|J=HfQQhC9=CEeD1Q0LpbKeadfgTu24$|s)R1qDs;HI5oS>#EcYTJp^rn5 zy%&R`7g3ye&0i2=9QxM$3Nv{BzJA2L5HZNgd{iFBsDu#?J!}3B_LKKf%1Ml|Z~PeP zCcK!$Dc0sn3R4`qNpj@+LTc{W$`bvE<%WhC$LTbONF+sc?T5!V6}{5gO*n0DyB6zB`IzjXDHE8duVTCmSPQa>A-vy7u&?3 zVdHY(s*MF3H%Qo^m2}{igFS81KY2QkuH(P6x2R@(M5lyLO~dv z=%Ih;#V`gjjUmj_DuoeT!zfnhxnJV(7(UWdd&uH8WQv=}&$01 delta 672 zcmY+B%W4yG6vn?vCY{b?V$)PG7d4Y<(k5+c5;dkyn;26AUP6rol;EX~Nvud;z!O3%JsaXHqxL;^RBF-#Pr}+vt|_LxI`A(?@s zf4q8`Nc>mIF6+$>;iUmeHit$fsp}BB0*>N1hhq%<#-`P86XAzs9I{?yWd@Tyr^j8q zbh&hOk#1bmsx@6e06`Hpgp3!~2tSDkhcJUs&*?Tq)I zWemXqV0#Z^S(j&VW)D7vr66T9h1*A<>|u8?uJ9gy2NQin_Yp69_+6Y-gdHS3LLX=Q zNSobuiu+g~HNJ3OHf@^kobSN=;k+B^kF?4Hfi!~=(h;z5g2NFy!VMR^B=QL33L?-b`yA7FM~`Ty>097Qw{epkh_r>