├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── lihuanghe │ ├── ParameterEval.java │ ├── ParameterPrepareStatement.java │ └── SqlTemplateParser.java └── test └── java └── com └── lihuanghe ├── DynamicScopes.java └── SqlTemplateParserTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .git 2 | .git/* 3 | .settings 4 | .settings/* 5 | bin 6 | target/ 7 | bin/* 8 | .classpath 9 | .project -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 | ## 一个简单的sql参数替换引擎,支持动态参数名,动态表名. 3 | ## 主要用在自动报表工具(引擎)上。可以支持sql设置参数 4 | 5 | # 用法: 6 | 7 | 8 | 1. 表名,参数名拼接 9 | 10 | > \#{pName#{pName}} : 参数pName值为5,首先解析成#{pName5},然后再取pName5的值替换。比如用于动态表名: select * from table_#{month} where id = ${id}. 11 | 12 | 2. 参数替换 13 | 14 | > ${parameter} : 替换为 ? ,同时设置对应位置的参数对象,供prepareStatement使用. 如果参数为空,则不替换。参数目前只支持String类型 15 | 16 | > ${parameter#{pName}} : 如果参数pName值为5,引擎首先解析成${parameter5},然后再取值。 17 | 如果有一个参数parameter5值为"hello world",则替换为?,且对应位置的参数值设置为"hello world" 18 | 19 | > @{arraysParameter} : 数组参数替换。如果数据不为空,且长度大于0 ,则替换为?,?,? .用于 sql的in , not in 语句,如: where id in ( 'Nouse',@{Ids} ) . 20 | 21 | > $[optParam: statment ] : 支持可选语句,如果参数optParam,值不为空,则解析[]内的statment,否则移除整条statment。用于传入可选参数: 22 | 23 | ```sql 24 | select * from shops 25 | where 1=1 26 | $[shopName: 27 | and shop_name = ${shopName} 28 | ] 29 | and status = 1 30 | ``` 31 | 32 | > @[optArrays: statment ] : 与$[]含义相同,但参数为数组,或者集合类型。仅当optArrays不为空,且长度大于0才执行statment。 33 | 34 | ```sql 35 | select * from shops 36 | where 1=1 37 | @[Ids: 38 | and id in ( 'Nouse',@{Ids} ) 39 | and beginDate = ${datetime} 40 | ] 41 | and status = 1 42 | ``` 43 | 44 | > $[optParam ? statementTrue: statementFalse ] : 支持三元表达式,仅当optParam存在,并且optParam不为空时,返回True. 45 | 46 | 3. 支持js处理参数。 47 | 48 | > 参数支持调用js进行处理,以'|'分开,前边是参数名,后边是js代码。 js代码可选。 如: 49 | 50 | ``` 51 | ${abc|DateFormat.format(abc,'yyyy-MM-dd')} 52 | 53 | @{b3|['hello'].concat(b3.join('*'))} 54 | 55 | ``` 56 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.lihuanghe 5 | sqlparse 6 | jar 7 | 0.0.1-SNAPSHOT 8 | sqlparse 9 | 10 | UTF-8 11 | true 12 | 1.7 13 | 1.7 14 | 1.7 15 | 1.7 16 | 17 | 18 | 19 | junit 20 | junit 21 | 4.11 22 | test 23 | 24 | 25 | commons-lang 26 | commons-lang 27 | 2.4 28 | 29 | 30 | 31 | 32 | 33 | maven-compiler-plugin 34 | 3.1 35 | 36 | 37 | default-testCompile 38 | test-compile 39 | 40 | testCompile 41 | 42 | 43 | ${java_source_version} 44 | ${java_target_version} 45 | true 46 | true 47 | ${project.build.sourceEncoding} 48 | 49 | 50 | 51 | default-compile 52 | compile 53 | 54 | compile 55 | 56 | 57 | ${java_source_version} 58 | ${java_target_version} 59 | true 60 | true 61 | ${project.build.sourceEncoding} 62 | 63 | 64 | 65 | 66 | ${java_source_version} 67 | ${java_target_version} 68 | true 69 | true 70 | ${project.build.sourceEncoding} 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/com/lihuanghe/ParameterEval.java: -------------------------------------------------------------------------------- 1 | package com.lihuanghe; 2 | 3 | import java.util.Map; 4 | 5 | import javax.script.Bindings; 6 | import javax.script.ScriptEngine; 7 | import javax.script.ScriptEngineManager; 8 | import javax.script.ScriptException; 9 | 10 | import org.apache.commons.lang.time.DateFormatUtils; 11 | import org.apache.commons.lang.time.DateUtils; 12 | 13 | public class ParameterEval { 14 | private boolean isArray = false; 15 | private String[] paramName; 16 | private String jsCode ; 17 | private static final ScriptEngineManager manager = new ScriptEngineManager(); 18 | private static final DateFormatUtils dataformat = new DateFormatUtils(); 19 | private static final DateUtils dateutils = new DateUtils(); 20 | 21 | /** 22 | *使用静态类型, JavaScriptEngine在jdk1.6的RhinoScriptEngine实现保证线程安全. 23 | */ 24 | private static final ScriptEngine engine = manager.getEngineByName("js"); 25 | 26 | public ParameterEval(String name,String code,boolean isArray) 27 | { 28 | this.isArray = isArray; 29 | this.paramName = name.split("\\."); 30 | this.jsCode = code; 31 | } 32 | public boolean isArray() { 33 | return isArray; 34 | } 35 | 36 | private Object executeJS(Map map) throws ScriptException 37 | { 38 | Bindings bd = engine.createBindings(); 39 | bd.put(paramName[paramName.length-1],getparaValue(map)); 40 | bd.put("DateFormat",dataformat); 41 | bd.put("DateUtils",dateutils); 42 | Object obj = engine.eval(jsCode,bd); 43 | return obj; 44 | } 45 | 46 | public Object getValueFromMap(Map map) 47 | { 48 | if(jsCode!=null) 49 | { 50 | try { 51 | return executeJS(map); 52 | } catch (ScriptException e) { 53 | e.printStackTrace(); 54 | return null; 55 | } 56 | } 57 | return getparaValue(map); 58 | } 59 | 60 | private Object getparaValue(Map map){ 61 | if(paramName.length == 1){ 62 | return map.get(paramName[0]); 63 | }else{ 64 | Map value = map; 65 | for(int i =0;i param) 38 | throws UnsupportedEncodingException { 39 | this.in = new InputStreamReader(in, charset); 40 | this.param = param; 41 | } 42 | 43 | private InputStreamReader in; 44 | private Map param; 45 | private int curChar = -1; 46 | private int prechar = -1; 47 | private int sqlpos = 0; // 用于记录当前读取到哪个字符 48 | private ParamType paramtype; // 用于区别当前要处理的参数类型是否是数组 49 | 50 | public class SqlParseException extends RuntimeException { 51 | 52 | static final long serialVersionUID = -7034897190745766939L; 53 | 54 | public SqlParseException(String s) { 55 | super(s); 56 | } 57 | } 58 | 59 | private enum ParamType { 60 | String, Array 61 | } 62 | 63 | private static SqlTemplateParser createParser(InputStream in, Map map, String charset) { 64 | try { 65 | return new SqlTemplateParser(in, charset == null ? "utf-8" : charset, map); 66 | } catch (UnsupportedEncodingException e) { 67 | return null; 68 | } 69 | } 70 | 71 | /** 72 | * @param sql 73 | * 待解析的sql 74 | * @param map 75 | * 存储参数变量 76 | * @param pstsParam 77 | * 用来存储解析之后的参数 78 | * @return sql 解析完成的sql 79 | */ 80 | public static String parseString(String sql, Map map, List pstsParam) 81 | throws SqlParseException, IOException { 82 | return parseString(sql, map, pstsParam, null); 83 | } 84 | 85 | /** 86 | * @param sql 87 | * 待解析的sql 88 | * @param map 89 | * 存储参数变量 90 | * @param pstsParam 91 | * 用来存储解析之后的参数 92 | * @return sql 解析完成的sql 93 | */ 94 | public static String parseString(String sql, Map map, List pstsParam, String charset) 95 | throws SqlParseException, IOException { 96 | InputStream in = null; 97 | 98 | try { 99 | in = new ByteArrayInputStream(sql.getBytes(charset == null ? "utf-8" : charset)); 100 | return parseString(in, map, pstsParam, charset); 101 | } catch (UnsupportedEncodingException e) { 102 | e.printStackTrace(); 103 | return null; 104 | } finally { 105 | if (in != null) { 106 | try { 107 | in.close(); 108 | } catch (IOException e) { 109 | 110 | } 111 | } 112 | } 113 | } 114 | 115 | /** 116 | * @param in 117 | * 从InputStream读取sql流 118 | * @param map 119 | * 存储参数变量 120 | * @param pstsParam 121 | * 用来存储解析之后的参数 122 | * @return sql 解析完成的sql 123 | */ 124 | public static String parseString(InputStream in, Map map, List pstsParam) 125 | throws SqlParseException, IOException { 126 | return parseString(in, map, pstsParam, null); 127 | } 128 | 129 | /** 130 | * @param in 131 | * 从InputStream读取sql流 132 | * @param map 133 | * 存储参数变量 134 | * @param pstsParam 135 | * 用来存储解析之后的参数 136 | * @return sql 解析完成的sql 137 | * @throws IOException 138 | */ 139 | public static String parseString(InputStream in, Map map, List pstsParam, String charset) 140 | throws SqlParseException, IOException { 141 | SqlTemplateParser parser = createParser(in, map, charset); 142 | return parser.parse(pstsParam); 143 | } 144 | 145 | /** 146 | * @param pstsParam 147 | * 用来存储解析之后的参数 148 | * @return sql 解析完成的sql 149 | */ 150 | protected String parse(List pstsParam) throws SqlParseException, IOException { 151 | return statmentUntilEnd(pstsParam); 152 | } 153 | 154 | protected String statmentUntilEnd(List pstsParam) throws IOException, SqlParseException { 155 | StringBuilder sqlbuf = new StringBuilder(); 156 | while ((curChar = readandsavepre()) != -1) { 157 | sqlbuf.append(statment(pstsParam)); 158 | } 159 | return sqlbuf.toString(); 160 | } 161 | 162 | protected String statment(List pstsParam) throws IOException, SqlParseException { 163 | 164 | switch (curChar) { 165 | case '$': 166 | paramtype = ParamType.String; 167 | return paramter(pstsParam); 168 | case '@': 169 | paramtype = ParamType.Array; 170 | return paramter(pstsParam); 171 | case '#': 172 | return parseConcat(); 173 | case '\\': 174 | return escape(); 175 | default: 176 | return String.valueOf((char) curChar); 177 | } 178 | } 179 | 180 | protected String escape() throws IOException, SqlParseException { 181 | curChar = readandsavepre(); 182 | StringBuilder escapestr = new StringBuilder(); 183 | switch (curChar) { 184 | case -1: 185 | throw new SqlParseException("expect escape char '\\' "); 186 | case '\\': 187 | case '|': 188 | case '@': 189 | case '$': 190 | case '#': 191 | case ':': 192 | case '?': 193 | case '{': 194 | case '}': 195 | case '[': 196 | case ']': 197 | escapestr.append((char) curChar); 198 | break; 199 | default: 200 | escapestr.append((char) prechar).append((char) curChar); 201 | } 202 | return escapestr.toString(); 203 | } 204 | 205 | protected String paramter(List pstsParam) throws IOException, SqlParseException { 206 | curChar = readandsavepre(); 207 | switch (curChar) { 208 | case -1: 209 | return String.valueOf((char) prechar); 210 | case '[': 211 | return optionalParameter(pstsParam); 212 | case '{': 213 | return requiredParameter(pstsParam); 214 | default: 215 | return new StringBuilder().append((char) prechar).append((char) curChar).toString(); 216 | } 217 | } 218 | 219 | private boolean isEmpty(ParameterEval paramName) { 220 | if (paramName.isArray()) { 221 | Object obj = paramName.getValueFromMap(param); 222 | 223 | if (obj == null) { 224 | return true; 225 | } 226 | 227 | Collection set = null; 228 | if (obj instanceof Collection) { 229 | set = (Collection) obj; 230 | } else if (obj instanceof String[]) { 231 | set = Arrays.asList((String[]) obj); 232 | } 233 | // 如果是集合类型,且长度为0 234 | if (set != null && set.size() == 0) { 235 | return true; 236 | } 237 | return false; 238 | } else { 239 | Object obj = paramName.getValueFromMap(param); 240 | String str = String.valueOf(obj == null ? "" : obj); 241 | if (StringUtils.isEmpty(str)) { 242 | return true; 243 | } else { 244 | return false; 245 | } 246 | } 247 | } 248 | 249 | protected String optionalParameter(List pstsParam) throws IOException, SqlParseException { 250 | // 获取参数 251 | ParameterEval paramName = readParamerUntil(new char[] { ':', '?' }); 252 | if (curChar == ':') { 253 | if (isEmpty(paramName)) { 254 | statmentUntil(']', new ArrayList()); 255 | // 丢弃statement 256 | return ""; 257 | } else { 258 | return statmentUntil(']', pstsParam); 259 | } 260 | } else if (curChar == '?') { 261 | if (isEmpty(paramName)) { 262 | statmentUntil(':', new ArrayList()); // 不执行的statement 263 | return statmentUntil(']', pstsParam); 264 | } else { 265 | String s = statmentUntil(':', pstsParam); 266 | 267 | statmentUntil(']', new ArrayList()); // 不执行的statement 268 | 269 | return s; 270 | } 271 | } 272 | throw new SqlParseException("something Error. never be here!!"); 273 | } 274 | 275 | protected String statmentUntil(int until, List pstsParam) throws IOException, 276 | SqlParseException { 277 | 278 | StringBuilder sqlbuf = new StringBuilder(); 279 | 280 | curChar = readandsavepre(); 281 | 282 | while (curChar != -1 && curChar != until) { 283 | sqlbuf.append(statment(pstsParam)); 284 | curChar = readandsavepre(); 285 | } 286 | 287 | String str = sqlbuf.toString(); 288 | if (curChar == -1) { 289 | throw new SqlParseException("\texpect char '" + (char) until + "' after statement : " +str); 290 | } 291 | return str; 292 | } 293 | 294 | // 处理必选参数 295 | protected String requiredParameter(List pstsParam) throws IOException, SqlParseException { 296 | 297 | // 获取参数名 298 | ParameterEval paramName = readParamerUntil(new char[] { '}' }); 299 | return addpstsParam(paramName, pstsParam); 300 | } 301 | 302 | private String addpstsParam(ParameterEval paramName, List pstsParam) { 303 | StringBuilder sqlbuf = new StringBuilder(); 304 | 305 | 306 | Object obj = paramName.getValueFromMap(param); 307 | 308 | //如果没有该参数,则忽略 309 | if(obj == null) return ""; 310 | 311 | Collection set = null; 312 | if (obj instanceof Collection) { 313 | set = (Collection) obj; 314 | } else if (obj instanceof String[]) { 315 | set = Arrays.asList((String[]) obj); 316 | } 317 | // 如果不是集合类型. 318 | if (set == null) { 319 | pstsParam.add(new ParameterPrepareStatement(sqlpos, String.valueOf(obj))); 320 | return "?"; 321 | } else { 322 | if (set.size() > 0) { 323 | for (Object s : set) { 324 | String p = String.valueOf(s); 325 | pstsParam.add(new ParameterPrepareStatement(sqlpos, p)); 326 | sqlbuf.append('?').append(','); 327 | } 328 | sqlbuf.deleteCharAt(sqlbuf.length() - 1); 329 | } 330 | return sqlbuf.toString(); 331 | } 332 | } 333 | 334 | private String parseConcat() throws IOException, SqlParseException { 335 | curChar = readandsavepre(); 336 | StringBuilder sqlbuf = new StringBuilder(); 337 | ParameterEval paramName = null; 338 | switch (curChar) { 339 | case -1: 340 | sqlbuf.append((char) prechar); 341 | break; 342 | case '{': 343 | paramName = readParamerUntil(new char[] { '}' }); 344 | break; 345 | default: 346 | sqlbuf.append((char) prechar).append((char) curChar); 347 | } 348 | 349 | // 已获取解析后的参数名 350 | if (paramName != null) { 351 | String tmp = (String) paramName.getValueFromMap(param); 352 | if (tmp != null) { 353 | sqlbuf.append(tmp); 354 | } 355 | } 356 | return sqlbuf.toString(); 357 | } 358 | 359 | private String readUntil(char[] c, boolean isparseParamName) throws IOException, SqlParseException { 360 | curChar = readandsavepre(); 361 | StringBuilder strbuf = new StringBuilder(); 362 | while (curChar != -1 && search(c, curChar) == -1) { 363 | 364 | if (isparseParamName) { 365 | strbuf.append(parseParameter()); 366 | } else { 367 | // 对于要丢弃的字符,不再解析 368 | strbuf.append('\\'==curChar ?escape():(char) curChar); 369 | } 370 | 371 | curChar = readandsavepre(); 372 | } 373 | if (curChar == -1) { 374 | throw new SqlParseException(strbuf.append( 375 | " :position[" + (sqlpos - strbuf.length()) + "]\t expect {" + Arrays.toString(c) + "}").toString()); 376 | } else { 377 | return strbuf.toString(); 378 | } 379 | } 380 | 381 | private String parseParameter() throws SqlParseException, IOException { 382 | switch (curChar) { 383 | case '\r': 384 | case '\n': 385 | throw new SqlParseException("parameter contains '\\r' ,'\\n' "); 386 | case '\\': 387 | return escape(); 388 | case '#': 389 | return parseConcat(); 390 | default: 391 | return String.valueOf((char) curChar); 392 | } 393 | } 394 | 395 | private int search(char[] chars, int c) { 396 | if (chars == null || chars.length == 0) 397 | return -1; 398 | for (int i = 0; i < chars.length; i++) { 399 | if (c == chars[i]) { 400 | return i; 401 | } 402 | } 403 | return -1; 404 | } 405 | 406 | private ParameterEval readParamerUntil(char[] chars) throws IOException, SqlParseException { 407 | curChar = readandsavepre(); 408 | StringBuilder strbuf = new StringBuilder(); 409 | while (curChar != -1 && curChar != '|' && search(chars, curChar) == -1) { 410 | String tmp = parseParameter(); 411 | strbuf.append(tmp); 412 | curChar = readandsavepre(); 413 | } 414 | // 去掉参数名的空格 415 | String name = strbuf.toString().trim(); 416 | // 参数名为空 417 | if (StringUtils.isEmpty(name)) { 418 | throw new SqlParseException(" after \"" + (char) prechar + (char) curChar 419 | + "\", Parameter Name is null at position : " + sqlpos); 420 | } 421 | 422 | if (curChar == -1) { 423 | throw new SqlParseException(strbuf.append( 424 | " :position[" + (sqlpos - strbuf.length()) + "]\t expect {" + Arrays.toString(chars) + "}") 425 | .toString()); 426 | } else if (curChar == '|') { 427 | 428 | // 读取filter内容,应该是一段可执行的js脚本 429 | String jsCode = readUntil(chars, false); 430 | 431 | return new ParameterEval(name, jsCode, ParamType.Array.equals(paramtype)); 432 | } else { 433 | return new ParameterEval(name, null, ParamType.Array.equals(paramtype)); 434 | } 435 | } 436 | 437 | private int readandsavepre() throws IOException { 438 | prechar = curChar; 439 | curChar = in.read(); 440 | sqlpos++; 441 | return curChar; 442 | } 443 | 444 | } -------------------------------------------------------------------------------- /src/test/java/com/lihuanghe/DynamicScopes.java: -------------------------------------------------------------------------------- 1 | package com.lihuanghe; 2 | 3 | /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 8 | 9 | import sun.org.mozilla.javascript.internal.Context; 10 | import sun.org.mozilla.javascript.internal.ContextFactory; 11 | import sun.org.mozilla.javascript.internal.Script; 12 | import sun.org.mozilla.javascript.internal.Scriptable; 13 | import sun.org.mozilla.javascript.internal.ScriptableObject; 14 | 15 | /** 16 | * Example of controlling the JavaScript with multiple scopes and threads. 17 | */ 18 | public class DynamicScopes { 19 | 20 | static boolean useDynamicScope; 21 | 22 | static class MyFactory extends ContextFactory 23 | { 24 | @Override 25 | protected boolean hasFeature(Context cx, int featureIndex) 26 | { 27 | if (featureIndex == Context.FEATURE_DYNAMIC_SCOPE) { 28 | return useDynamicScope; 29 | } 30 | return super.hasFeature(cx, featureIndex); 31 | } 32 | } 33 | 34 | static { 35 | ContextFactory.initGlobal(new MyFactory()); 36 | } 37 | 38 | 39 | /** 40 | * Main entry point. 41 | * 42 | * Set up the shared scope and then spawn new threads that execute 43 | * relative to that shared scope. Try to run functions with and 44 | * without dynamic scope to see the effect. 45 | * 46 | * The expected output is 47 | *
 48 |     * sharedScope
 49 |     * nested:sharedScope
 50 |     * sharedScope
 51 |     * nested:sharedScope
 52 |     * sharedScope
 53 |     * nested:sharedScope
 54 |     * thread0
 55 |     * nested:thread0
 56 |     * thread1
 57 |     * nested:thread1
 58 |     * thread2
 59 |     * nested:thread2
 60 |     * 
61 | * The final three lines may be permuted in any order depending on 62 | * thread scheduling. 63 | */ 64 | public static void main(String[] args) 65 | { 66 | Context cx = Context.enter(); 67 | try { 68 | // Precompile source only once 69 | String source = "" 70 | +"var x = 'sharedScope';\n" 71 | +"function f() { return x; }\n" 72 | // Dynamic scope works with nested function too 73 | +"function initClosure(prefix) {\n" 74 | +" return function test() { return prefix+x; }\n" 75 | +"}\n" 76 | +"var closure = initClosure('nested:');\n" 77 | +""; 78 | Script script = cx.compileString(source, "sharedScript", 1, null); 79 | 80 | useDynamicScope = false; 81 | runScripts(cx, script); 82 | useDynamicScope = true; 83 | runScripts(cx, script); 84 | } finally { 85 | Context.exit(); 86 | } 87 | } 88 | 89 | static void runScripts(Context cx, Script script) 90 | { 91 | // Initialize the standard objects (Object, Function, etc.) 92 | // This must be done before scripts can be executed. The call 93 | // returns a new scope that we will share. 94 | ScriptableObject sharedScope = cx.initStandardObjects(null, true); 95 | 96 | // Now we can execute the precompiled script against the scope 97 | // to define x variable and f function in the shared scope. 98 | script.exec(cx, sharedScope); 99 | 100 | // Now we spawn some threads that execute a script that calls the 101 | // function 'f'. The scope chain looks like this: 102 | //
103 |        //            ------------------                ------------------
104 |        //           | per-thread scope | -prototype-> |   shared scope   |
105 |        //            ------------------                ------------------
106 |        //                    ^
107 |        //                    |
108 |        //               parentScope
109 |        //                    |
110 |        //            ------------------
111 |        //           | f's activation   |
112 |        //            ------------------
113 |        // 
114 | // Both the shared scope and the per-thread scope have variables 'x' 115 | // defined in them. If 'f' is compiled with dynamic scope enabled, 116 | // the 'x' from the per-thread scope will be used. Otherwise, the 'x' 117 | // from the shared scope will be used. The 'x' defined in 'g' (which 118 | // calls 'f') should not be seen by 'f'. 119 | final int threadCount = 3; 120 | Thread[] t = new Thread[threadCount]; 121 | for (int i=0; i < threadCount; i++) { 122 | String source2 = "" 123 | +"function g() { var x = 'local'; return f(); }\n" 124 | +"java.lang.System.out.println(g());\n" 125 | +"function g2() { var x = 'local'; return closure(); }\n" 126 | +"java.lang.System.out.println(g2());\n" 127 | +""; 128 | t[i] = new Thread(new PerThread(sharedScope, source2, 129 | "thread" + i)); 130 | } 131 | for (int i=0; i < threadCount; i++) 132 | t[i].start(); 133 | // Don't return in this thread until all the spawned threads have 134 | // completed. 135 | for (int i=0; i < threadCount; i++) { 136 | try { 137 | t[i].join(); 138 | } catch (InterruptedException e) { 139 | } 140 | } 141 | } 142 | 143 | static class PerThread implements Runnable { 144 | 145 | PerThread(Scriptable sharedScope, String source, String x) { 146 | this.sharedScope = sharedScope; 147 | this.source = source; 148 | this.x = x; 149 | } 150 | 151 | public void run() { 152 | // We need a new Context for this thread. 153 | Context cx = Context.enter(); 154 | try { 155 | // We can share the scope. 156 | Scriptable threadScope = cx.newObject(sharedScope); 157 | threadScope.setPrototype(sharedScope); 158 | 159 | // We want "threadScope" to be a new top-level 160 | // scope, so set its parent scope to null. This 161 | // means that any variables created by assignments 162 | // will be properties of "threadScope". 163 | threadScope.setParentScope(null); 164 | 165 | // Create a JavaScript property of the thread scope named 166 | // 'x' and save a value for it. 167 | threadScope.put("x", threadScope, x); 168 | cx.evaluateString(threadScope, source, "threadScript", 1, null); 169 | } finally { 170 | Context.exit(); 171 | } 172 | } 173 | private Scriptable sharedScope; 174 | private String source; 175 | private String x; 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /src/test/java/com/lihuanghe/SqlTemplateParserTest.java: -------------------------------------------------------------------------------- 1 | package com.lihuanghe; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.Date; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import org.apache.commons.lang.time.DateFormatUtils; 12 | import org.junit.Assert; 13 | import org.junit.Test; 14 | 15 | import com.lihuanghe.SqlTemplateParser.SqlParseException; 16 | 17 | 18 | public class SqlTemplateParserTest { 19 | 20 | @Test 21 | public void testsql() throws SqlParseException, IOException 22 | { 23 | String sql = "select * from shops_#{month} \nwhere 1=1 @[Ids: \nand id in ('Nouse',@{Ids}) ] \nand status = 1"; 24 | Map map = new HashMap(); 25 | map.put("Ids", new String[]{"2","3","4"}); 26 | map.put("month", "201503"); 27 | List param = new ArrayList(); 28 | String pstsSql = SqlTemplateParser.parseString(sql, map, param); 29 | String expect = "select * from shops_201503 \nwhere 1=1 \nand id in ('Nouse',?,?,?) \nand status = 1"; 30 | System.out.println(Arrays.toString(param.toArray())); 31 | System.out.println(expect); 32 | 33 | Assert.assertEquals(expect, pstsSql); 34 | Assert.assertEquals(3, param.size()); 35 | Assert.assertEquals(Arrays.toString(new String[]{"2" ,"3" ,"4" }), Arrays.toString(param.toArray())); 36 | 37 | } 38 | @Test 39 | public void testall() throws SqlParseException, IOException 40 | { 41 | String sql = "begin #{ m1.p3 } ${p1},@{p2},p#{p1},$[p1: midle1:${p1}],\n $[p5: midle2:${p1}],@[p2:midle3:@{p2}],@[p5:midle4:@{p2}],\\${,\\a,\\\\ end$"; 42 | Map map = new HashMap(); 43 | map.put("p1", "1"); 44 | map.put("p3", "sdf"); 45 | map.put("p2", new String[]{"2","3","4"}); 46 | map.put("m1", new HashMap(map)); 47 | List param = new ArrayList(); 48 | String pstsSql = SqlTemplateParser.parseString(sql, map, param); 49 | String expect = "begin sdf ?,?,?,?,p1, midle1:?,\n ,midle3:?,?,?,,${,\\a,\\ end$"; 50 | Assert.assertEquals(expect, pstsSql); 51 | Assert.assertEquals(8, param.size()); 52 | Assert.assertEquals(Arrays.toString(new String[]{"1" ,"2" ,"3" ,"4" ,"1" ,"2" ,"3" ,"4" }), Arrays.toString(param.toArray())); 53 | } 54 | 55 | //末尾的转义异常 56 | @Test 57 | public void testlost4() 58 | { 59 | String sql = "begin end\\"; 60 | Map map = new HashMap(); 61 | List param = new ArrayList(); 62 | try { 63 | String pstsSql = SqlTemplateParser.parseString(sql, map, param); 64 | Assert.assertTrue(false); 65 | } catch (SqlParseException e) { 66 | Assert.assertTrue(true); 67 | e.printStackTrace(); 68 | } catch (IOException e) { 69 | Assert.assertTrue(false); 70 | e.printStackTrace(); 71 | } 72 | } 73 | 74 | //末尾缺少符号 75 | @Test 76 | public void testlost5() 77 | { 78 | String sql = "begin $[lost5: asff end"; 79 | Map map = new HashMap(); 80 | List param = new ArrayList(); 81 | try { 82 | String pstsSql = SqlTemplateParser.parseString(sql, map, param); 83 | Assert.assertTrue(false); 84 | } catch (SqlParseException e) { 85 | Assert.assertTrue(true); 86 | e.printStackTrace(); 87 | } catch (IOException e) { 88 | Assert.assertTrue(false); 89 | e.printStackTrace(); 90 | } 91 | } 92 | //参数缺少大括号 93 | @Test 94 | public void testlost1() throws SqlParseException, IOException 95 | { 96 | String sql = "begin ${p1,@{p2},p#{p1},$[p1: midle1:${p1}], $[p5: midle2:${p1}],@[p2:midle3:@{p2}],@[p5:midle4:@{p2}],\\${,\\a,\\\\ $abc #XY end"; 97 | Map map = new HashMap(); 98 | map.put("p1", "1"); 99 | map.put("p2", new String[]{"2","3","4"}); 100 | List param = new ArrayList(); 101 | String pstsSql = SqlTemplateParser.parseString(sql, map, param); 102 | String expect = "begin ,p1, midle1:?, ,midle3:?,?,?,,${,\\a,\\ $abc #XY end"; 103 | Assert.assertEquals(expect, pstsSql); 104 | Assert.assertEquals(4, param.size()); 105 | Assert.assertEquals(Arrays.toString(new String[]{"1" ,"2" ,"3" ,"4" }),Arrays.toString( param.toArray())); 106 | } 107 | 108 | //参数缺少大括号 109 | @Test 110 | public void testlost2() throws IOException 111 | { 112 | String sql = "begin ${p1},@{p2},p#{p1},$[p1: midle1:${p1}], $[p5: midle2:${p1}],@[p5:midle3:@{p2}],@[p2:midle4:@{p2],\\${,\\a,\\\\ end"; 113 | Map map = new HashMap(); 114 | map.put("p1", "1"); 115 | map.put("p2", new String[]{"2","3","4"}); 116 | List param = new ArrayList(); 117 | String pstsSql=null; 118 | try { 119 | pstsSql = SqlTemplateParser.parseString(sql, map, param); 120 | Assert.assertTrue(false); 121 | } catch (SqlParseException e) { 122 | e.printStackTrace(); 123 | Assert.assertTrue(true); 124 | } 125 | Assert.assertEquals((String)null, pstsSql); 126 | } 127 | 128 | //参数名为空 129 | @Test 130 | public void testparamNameisNull() throws IOException 131 | { 132 | String sql = "begin ${#{p1}} ${#{p2}} end"; 133 | Map map = new HashMap(); 134 | map.put("p1", "1"); 135 | map.put("p2", ""); 136 | List param = new ArrayList(); 137 | String pstsSql=null; 138 | try { 139 | pstsSql = SqlTemplateParser.parseString(sql, map, param); 140 | System.out.println(pstsSql); 141 | Assert.assertTrue(false); 142 | } catch (SqlParseException e) { 143 | e.printStackTrace(); 144 | Assert.assertTrue(true); 145 | } 146 | Assert.assertEquals((String)null, pstsSql); 147 | } 148 | 149 | //参数缺少大括号 150 | @Test 151 | public void testlost3() throws IOException 152 | { 153 | String sql = "begin ${p1},@{p2},p#{p1},$[p1: midle1:${p1}], $[p5: midle2:${p1}],@[p5:midle3:@{p2}],@[p2:midle4:@{p2},\\${,\\a,\\\\ end"; 154 | Map map = new HashMap(); 155 | map.put("p1", "1"); 156 | map.put("p2", new String[]{"2","3","4"}); 157 | List param = new ArrayList(); 158 | String pstsSql=null; 159 | try { 160 | pstsSql = SqlTemplateParser.parseString(sql, map, param); 161 | Assert.assertTrue(false); 162 | } catch (SqlParseException e) { 163 | e.printStackTrace(); 164 | Assert.assertTrue(true); 165 | } 166 | Assert.assertEquals((String)null, pstsSql); 167 | } 168 | //参数缺少大括号 169 | @Test 170 | public void testparamNameContain() throws IOException 171 | { 172 | String sql = "begin ${p1\n} end"; 173 | Map map = new HashMap(); 174 | map.put("p1", "1"); 175 | map.put("p2", new String[]{"2","3","4"}); 176 | List param = new ArrayList(); 177 | String pstsSql=null; 178 | try { 179 | pstsSql = SqlTemplateParser.parseString(sql, map, param); 180 | Assert.assertTrue(false); 181 | } catch (SqlParseException e) { 182 | e.printStackTrace(); 183 | Assert.assertTrue(true); 184 | } 185 | Assert.assertEquals((String)null, pstsSql); 186 | } 187 | @Test 188 | public void testconcat() throws SqlParseException, IOException 189 | { 190 | String sql = "begin ${p4} ${ p1 },@{\t p2 \t },${p#{p1}\t},#{p#{p1}},$[p1: midle1:${p1},#{p#{p1}} ],\n $[p5: midle2:${p1}],@[p2:midle3:@{p2}],@[p5:midle4:@{p2}],\\${,\\a,\\\\ end#"; 191 | Map map = new HashMap(); 192 | map.put("p1", "3"); 193 | map.put("p3", "1"); 194 | map.put("p4", ""); 195 | map.put("p2", new String[]{"2","3","4"}); 196 | List param = new ArrayList(); 197 | String pstsSql = SqlTemplateParser.parseString(sql, map, param); 198 | String expect = "begin ? ?,?,?,?,?,1, midle1:?,1 ,\n ,midle3:?,?,?,,${,\\a,\\ end#"; 199 | Assert.assertEquals(expect, pstsSql); 200 | Assert.assertEquals(10, param.size()); 201 | Assert.assertEquals(Arrays.toString(new String[]{"","3" ,"2" ,"3" ,"4" ,"1","3" ,"2" ,"3" ,"4" }), Arrays.toString(param.toArray())); 202 | } 203 | @Test 204 | public void testCNcode() throws SqlParseException, IOException 205 | { 206 | String sql = "开始 ${参数1},@{参数2},参数#{参数1},$[参数1: 中间1:${参数1}], $[参数5: 中间2:${参数1}],@[参数2:中间3:@{参数2}],@[参数5:midle4:@{参数2}],\\${,\\a,\\\\ 结束"; 207 | Map map = new HashMap(); 208 | map.put("参数1", "1"); 209 | map.put("参数2", new String[]{"2","3","4"}); 210 | List param = new ArrayList(); 211 | String pstsSql = SqlTemplateParser.parseString(sql, map, param); 212 | String expect = "开始 ?,?,?,?,参数1, 中间1:?, ,中间3:?,?,?,,${,\\a,\\ 结束"; 213 | Assert.assertEquals(expect, pstsSql); 214 | Assert.assertEquals(8, param.size()); 215 | Assert.assertEquals(Arrays.toString(new String[]{"1" ,"2" ,"3" ,"4" ,"1" ,"2" ,"3" ,"4" }), Arrays.toString(param.toArray())); 216 | } 217 | 218 | //测试js执行 219 | @Test 220 | public void testJscode() throws SqlParseException, IOException 221 | { 222 | Date d = new Date(); 223 | String sql = "begin ${b3|[]} @[b3|[]: abc ] ${abc|if(abc) {\nabc?DateFormat.format(abc,'yyyy-MM-dd'):'error null';\n\\}} ${abcd|abcd&&DateFormat.format(abcd,'yyyy-MM-dd')} (@{b3|['hello'].concat(b3.join('*'))}) #{abc|DateFormat.format(abc,'yyyy-MM-dd')} #{beginDate|DateFormat.format(DateUtils.addMonths(DateUtils.parseDate(beginDate, ['yyyy-MM-dd']),-1),'yyyyMM')} (${b2|b2.addAll(['5','6','7']);b2}) end "; 224 | Map map = new HashMap(); 225 | map.put("beginDate","2015-03-23"); 226 | map.put("abc", d); 227 | map.put("b3", new String[]{"2","3","4"}); 228 | List tmp = new ArrayList(); 229 | tmp.addAll(Arrays.asList(new String[]{"2","3","4"})); 230 | map.put("b2", tmp); 231 | 232 | List param = new ArrayList(); 233 | String pstsSql = SqlTemplateParser.parseString(sql, map, param,"utf-8"); 234 | System.out.println(pstsSql); 235 | System.out.println(Arrays.toString(param.toArray())); 236 | 237 | String str = DateFormatUtils.format(d,"yyyy-MM-dd"); 238 | String expect = "begin ? (?,?) "+str+" 201502 (?,?,?,?,?,?) end "; 239 | Assert.assertEquals(expect, pstsSql); 240 | Assert.assertEquals(9, param.size()); 241 | Assert.assertEquals(Arrays.toString(new String[]{str,"hello","2*3*4" ,"2","3","4","5","6","7"}), Arrays.toString(param.toArray())); 242 | } 243 | 244 | @Test 245 | public void testinnerOptionalParameter()throws SqlParseException, IOException 246 | { 247 | String sql = "begin $[p1: abc $[p3: def(${p2})]] $[p4: abc $[p3: def(${p2})]] end"; 248 | Map map = new HashMap(); 249 | map.put("p1", "3"); 250 | map.put("p3", "1"); 251 | map.put("p4", ""); 252 | map.put("p2", new String[]{"2","3","4"}); 253 | List param = new ArrayList(); 254 | String pstsSql = SqlTemplateParser.parseString(sql, map, param); 255 | String expect = "begin abc def(?,?,?) end"; 256 | Assert.assertEquals(expect, pstsSql); 257 | Assert.assertEquals(3, param.size()); 258 | Assert.assertEquals(Arrays.toString(new String[]{"2" ,"3" ,"4"}), Arrays.toString(param.toArray())); 259 | 260 | } 261 | 262 | @Test 263 | public void testThreeMetaParameter()throws SqlParseException, IOException 264 | { 265 | String sql = "begin $[ p1 ? abc : def] $[ p2 ? abc : def] end"; 266 | Map map = new HashMap(); 267 | map.put("p1", "3"); 268 | List param = new ArrayList(); 269 | String pstsSql = SqlTemplateParser.parseString(sql, map, param); 270 | String expect = "begin abc def end"; 271 | Assert.assertEquals(expect, pstsSql); 272 | } 273 | 274 | //测试js执行性能 275 | @Test 276 | public void testJsperf() throws SqlParseException, IOException 277 | { 278 | int i = 0; 279 | Date d = new Date(); 280 | String sql = "begin ${p1,@{p2},p#{p1},$[p1: midle1:${p1}], $[p5: midle2:${p1}],@[p2:midle3:@{p2}],@[p5:midle4:@{p2}],\\${,\\a,\\\\ $abc #XY end"; 281 | Map map = new HashMap(); 282 | map.put("abc", d); 283 | List param = new ArrayList(); 284 | int cnt = 100000; 285 | long st = System.currentTimeMillis(); 286 | for(;i< cnt;i++){ 287 | SqlTemplateParser.parseString(sql, map, param); 288 | }; 289 | long ed = System.currentTimeMillis(); 290 | System.out.println("totle:"+(double)(ed - st)+"ms ; per "+(double)(ed - st)/cnt +"ms"); 291 | } 292 | } 293 | --------------------------------------------------------------------------------