diff --git a/pom.xml b/pom.xml index b2d1e9c..d2e7e69 100644 --- a/pom.xml +++ b/pom.xml @@ -86,8 +86,8 @@ maven-compiler-plugin 3.6.1 - 1.8 - 1.8 + 14 + 14 UTF-8 diff --git a/src/main/java/com/jfinal/kit/TplKit.java b/src/main/java/com/jfinal/kit/TplKit.java new file mode 100644 index 0000000..1994535 --- /dev/null +++ b/src/main/java/com/jfinal/kit/TplKit.java @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jfinal.kit; + + +import com.jfinal.kit.tpl.NameSpaceDirective; +import com.jfinal.kit.tpl.ParaDirective; +import com.jfinal.kit.tpl.TplDirective; +import com.jfinal.kit.tpl.TplSource; +import com.jfinal.template.Engine; +import com.jfinal.template.Template; +import com.jfinal.template.source.FileSource; +import com.jfinal.template.source.ISource; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * SqlKit + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +public class TplKit { + public static String baseSqlTemplatePath; + public static final String SQL_TEMPLATE_MAP_KEY = "_SQL_TEMPLATE_MAP_"; + public static final String SQL_PARA_KEY = "_SQL_PARA_"; + public static final String PARA_ARRAY_KEY = "_PARA_ARRAY_"; // 此参数保持不动,已被用于模板取值 _PARA_ARRAY_[n] + + private String configName; + private boolean devMode; + private Engine engine; + private List tplSourceList = new ArrayList(); + private Map sqlTemplateMap; + + private static Map kitMap = new HashMap<>(); + + public static TplKit use(String configName) { + return use(configName, false); + } + public static synchronized TplKit use(String configName, boolean devMode) { + TplKit tplKit = kitMap.get(configName); + if (tplKit == null) { + tplKit = new TplKit(configName, devMode); + kitMap.put(configName, tplKit); + } + return tplKit; + } + + private TplKit(String configName, boolean devMode) { + this.configName = configName; + this.devMode = devMode; + + engine = new Engine(configName); + engine.setDevMode(devMode); + engine.setToClassPathSourceFactory(); + + engine.addDirective("namespace", NameSpaceDirective.class); + engine.addDirective("tpl", TplDirective.class); + + engine.addDirective("para", ParaDirective.class, true); + engine.addDirective("p", ParaDirective.class, true); // 配置 #para 指令的别名指令 #p,不建议使用,在此仅为兼容 3.0 版本 + } + + private TplKit(String configName) { + this(configName, false); + } + + public Engine getEngine() { + return engine; + } + + public void setDevMode(boolean devMode) { + this.devMode = devMode; + engine.setDevMode(devMode); + } + + public void setBaseTplTemplatePath(String baseSqlTemplatePath) { + this.baseSqlTemplatePath = baseSqlTemplatePath; + } + + public void addTplTemplate(String sqlTemplate) { + if (StrKit.isBlank(sqlTemplate)) { + throw new IllegalArgumentException("tplTemplate can not be blank"); + } + if (baseSqlTemplatePath != null) { + addTplTemplate(new FileSource(baseSqlTemplatePath, "tpl.sql")); + } else { + tplSourceList.add(new TplSource(sqlTemplate)); + } + } + + public void addTplTemplate(ISource sqlTemplate) { + if (sqlTemplate == null) { + throw new IllegalArgumentException("sqlTemplate can not be null"); + } + tplSourceList.add(new TplSource(sqlTemplate)); + } + + public void addTplTemplate(File file) { + addTplTemplate(file, null); + } + + public void addTplTemplate(File file, FileFilter filter) { + if (file.isFile()) { + if (filter != null && !filter.accept(file)) { + return; + } + addTplTemplate(new FileSource(file.getParent(), file.getName())); + } else if (file.isDirectory()) { + File[] files = file.listFiles(); + for (File file1 : files) { + addTplTemplate(file1, filter); + } + } + } + + public synchronized void parseTplTemplate() { + Map sqlTemplateMap = new HashMap(512, 0.5F); + for (TplSource ss : tplSourceList) { + Template template = ss.isFile() ? engine.getTemplate(ss.file) : engine.getTemplate(ss.source); + Map data = new HashMap(); + data.put(SQL_TEMPLATE_MAP_KEY, sqlTemplateMap); + template.renderToString(data); + } + this.sqlTemplateMap = sqlTemplateMap; + } + + private void reloadModifiedTplTemplate() { + engine.removeAllTemplateCache(); // 去除 Engine 中的缓存,以免 get 出来后重新判断 isModified + parseTplTemplate(); + } + + private boolean isTplTemplateModified() { + for (Template template : sqlTemplateMap.values()) { + if (template.isModified()) { + return true; + } + } + return false; + } + + private Template getTplTemplate(String key) { + Template template = sqlTemplateMap.get(key); + if (template == null) { // 此 if 分支,处理起初没有定义,但后续不断追加 sql 的情况 + if (!devMode) { + return null; + } + if (isTplTemplateModified()) { + synchronized (this) { + if (isTplTemplateModified()) { + reloadModifiedTplTemplate(); + template = sqlTemplateMap.get(key); + } + } + } + return template; + } + + if (devMode && template.isModified()) { + synchronized (this) { + template = sqlTemplateMap.get(key); + if (template.isModified()) { + reloadModifiedTplTemplate(); + template = sqlTemplateMap.get(key); + } + } + } + return template; + } + + public String getTpl(String key) { + Template template = getTplTemplate(key); + return template != null ? template.renderToString(null).replaceAll("[\\s]+", " ") : null; + } + + public String getTpl(String key, Map para) { + Template template = getTplTemplate(key); + return template != null ? template.renderToString(para).replaceAll("[\\s]+", " ") : null; + } + + public String toString() { + return "SqlKit for config : " + configName; + } + +} + + + + diff --git a/src/main/java/com/jfinal/kit/tpl/NameSpaceDirective.java b/src/main/java/com/jfinal/kit/tpl/NameSpaceDirective.java new file mode 100644 index 0000000..d15f20c --- /dev/null +++ b/src/main/java/com/jfinal/kit/tpl/NameSpaceDirective.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jfinal.kit.tpl; + +import com.jfinal.template.Directive; +import com.jfinal.template.Env; +import com.jfinal.template.TemplateException; +import com.jfinal.template.expr.ast.Const; +import com.jfinal.template.expr.ast.Expr; +import com.jfinal.template.expr.ast.ExprList; +import com.jfinal.template.io.Writer; +import com.jfinal.template.stat.ParseException; +import com.jfinal.template.stat.Scope; + +/** + * NameSpaceDirective + */ +public class NameSpaceDirective extends Directive { + + static final String NAME_SPACE_KEY = "_NAME_SPACE_"; + + private String nameSpace; + + public void setExprList(ExprList exprList) { + if (exprList.length() == 0) { + throw new ParseException("The parameter of #namespace directive can not be blank", location); + } + if (exprList.length() > 1) { + throw new ParseException("Only one parameter allowed for #namespace directive", location); + } + Expr expr = exprList.getExpr(0); + if (expr instanceof Const && ((Const)expr).isStr()) { + } else { + throw new ParseException("The parameter of #namespace directive must be String", location); + } + + this.nameSpace = ((Const)expr).getStr(); + } + + public void exec(Env env, Scope scope, Writer writer) { + if (scope.get(NAME_SPACE_KEY) != null) { + throw new TemplateException("#namespace directive can not be nested", location); + } + scope.set(NAME_SPACE_KEY, nameSpace); + try { + stat.exec(env, scope, writer); + } finally { + scope.remove(NAME_SPACE_KEY); + } + } + + public boolean hasEnd() { + return true; + } +} + + + + + diff --git a/src/main/java/com/jfinal/kit/tpl/ParaDirective.java b/src/main/java/com/jfinal/kit/tpl/ParaDirective.java new file mode 100644 index 0000000..c7d96b7 --- /dev/null +++ b/src/main/java/com/jfinal/kit/tpl/ParaDirective.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jfinal.kit.tpl; + +import com.jfinal.kit.TplKit; +import com.jfinal.template.Directive; +import com.jfinal.template.Env; +import com.jfinal.template.TemplateException; +import com.jfinal.template.expr.ast.Const; +import com.jfinal.template.expr.ast.Expr; +import com.jfinal.template.expr.ast.ExprList; +import com.jfinal.template.expr.ast.Id; +import com.jfinal.template.io.Writer; +import com.jfinal.template.stat.ParseException; +import com.jfinal.template.stat.Scope; + +/** + * #para 指令用于在 sql 模板中根据参数名生成问号占位以及查询参数 + * + *

+ * 一、参数为表达式的用法
+ * 1:模板内容
+ *   #sql("find")
+ *     select * from user where nickName = #para(nickName) and age > #para(age)
+ *   #end
+ *   
+ * 2: java 代码
+ *   SqlPara sp = getSqlPara("find", Kv.by("nickName", "prettyGirl").set("age", 18));
+ *   user.find(sp)
+ *   或者:
+ *   user.find(sp.getSql(), sp.getPara());
+ * 
+ * 3:以上用法会在 #para(expr) 处生成问号占位字符,并且实际的参数放入 SqlPara 对象的参数列表中
+ *   后续可以通过 sqlPara.getPara() 获取到参数并直接用于查询
+ * 
+ * 
+ * 二、参数为 int 型数字的用法
+ * 1:模板内容
+ *   #sql("find")
+ *     select * from user where id > #para(0) and id < #para(1)
+ *   #end
+ *   
+ * 2: java 代码
+ *   SqlPara sp = getSqlPara("find", 10, 100);
+ *   user.find(sp)
+ * 
+ * 3:以上用法会在 #para(0) 与 #para(1) 处生成问号占位字符,并且将 10、100 这两个参数放入
+ *    SqlPara 对象的参数列表中,后续可以通过 sqlPara.getPara() 获取到参数并直接用于查询
+ * 
+ */ +public class ParaDirective extends Directive { + + private int index = -1; + private String paraName = null; + private static boolean checkParaAssigned = true; + + public static void setCheckParaAssigned(boolean checkParaAssigned) { + ParaDirective.checkParaAssigned = checkParaAssigned; + } + + public void setExprList(ExprList exprList) { + if (exprList.length() == 0) { + throw new ParseException("The parameter of #para directive can not be blank", location); + } + + if (exprList.length() == 1) { + Expr expr = exprList.getExpr(0); + if (expr instanceof Const && ((Const)expr).isInt()) { + index = ((Const)expr).getInt(); + if (index < 0) { + throw new ParseException("The index of para array must greater than -1", location); + } + } + } + + if (checkParaAssigned && exprList.getLastExpr() instanceof Id) { + Id id = (Id)exprList.getLastExpr(); + paraName = id.getId(); + } + + this.exprList = exprList; + } + + public void exec(Env env, Scope scope, Writer writer) { + TplPara tplPara = (TplPara)scope.get(TplKit.SQL_PARA_KEY); + if (tplPara == null) { + throw new TemplateException("#para directive invoked by getSqlPara(...) method only", location); + } + + write(writer, "?"); + if (index == -1) { + // #para(paraName) 中的 paraName 没有赋值时抛出异常 + // issue: http://www.jfinal.com/feedback/1832 + if (checkParaAssigned && paraName != null && !scope.exists(paraName)) { + throw new TemplateException("The parameter \""+ paraName +"\" must be assigned", location); + } + + tplPara.addPara(exprList.eval(scope)); + } else { + Object[] paras = (Object[])scope.get(TplKit.PARA_ARRAY_KEY); + if (paras == null) { + throw new TemplateException("The #para(" + index + ") directive must invoked by getSqlPara(String, Object...) method", location); + } + if (index >= paras.length) { + throw new TemplateException("The index of #para directive is out of bounds: " + index, location); + } + tplPara.addPara(paras[index]); + } + } +} + + + diff --git a/src/main/java/com/jfinal/kit/tpl/TplDirective.java b/src/main/java/com/jfinal/kit/tpl/TplDirective.java new file mode 100644 index 0000000..3a548c7 --- /dev/null +++ b/src/main/java/com/jfinal/kit/tpl/TplDirective.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jfinal.kit.tpl; + +import com.jfinal.kit.StrKit; +import com.jfinal.kit.TplKit; +import com.jfinal.template.Directive; +import com.jfinal.template.Env; +import com.jfinal.template.Template; +import com.jfinal.template.expr.ast.Const; +import com.jfinal.template.expr.ast.Expr; +import com.jfinal.template.expr.ast.ExprList; +import com.jfinal.template.io.Writer; +import com.jfinal.template.stat.ParseException; +import com.jfinal.template.stat.Scope; + +import java.util.Map; + +/** + * SqlDirective + */ +public class TplDirective extends Directive { + + private String id; + + public void setExprList(ExprList exprList) { + if (exprList.length() == 0) { + throw new ParseException("The parameter of #sql directive can not be blank", location); + } + if (exprList.length() > 1) { + throw new ParseException("Only one parameter allowed for #sql directive", location); + } + Expr expr = exprList.getExpr(0); + if (expr instanceof Const && ((Const)expr).isStr()) { + } else { + throw new ParseException("The parameter of #sql directive must be String", location); + } + + this.id = ((Const)expr).getStr(); + } + + @SuppressWarnings("unchecked") + public void exec(Env env, Scope scope, Writer writer) { + String nameSpace = (String)scope.get(NameSpaceDirective.NAME_SPACE_KEY); + String key = StrKit.isBlank(nameSpace) ? id : nameSpace + "." + id; + Map sqlTemplateMap = (Map)scope.get(TplKit.SQL_TEMPLATE_MAP_KEY); + if (sqlTemplateMap.containsKey(key)) { + throw new ParseException("Sql already exists with key : " + key, location); + } + + sqlTemplateMap.put(key, new Template(env, stat)); + } + + public boolean hasEnd() { + return true; + } +} + + + diff --git a/src/main/java/com/jfinal/kit/tpl/TplPara.java b/src/main/java/com/jfinal/kit/tpl/TplPara.java new file mode 100644 index 0000000..dbced35 --- /dev/null +++ b/src/main/java/com/jfinal/kit/tpl/TplPara.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jfinal.kit.tpl; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * SqlPara + * 封装查询使用的 sql 与参数,主要用于 getSqlPara(...) 返回值 + */ +public class TplPara implements Serializable { + + private static final long serialVersionUID = -8586448059592782381L; + + String sql; + List paraList; + + public TplPara setSql(String sql) { + this.sql = sql; + return this; + } + + public TplPara addPara(Object para) { + if (paraList == null) { + paraList = new ArrayList(); + } + paraList.add(para); + return this; + } + + public String getSql() { + return sql; + } + + public Object[] getPara() { + /*if (paraList == null || paraList.size() == 0) { + return DbKit.NULL_PARA_ARRAY; + } else { + return paraList.toArray(new Object[paraList.size()]); + }*/ + return null; + } + + public TplPara clear() { + sql = null; + if (paraList != null) { + paraList.clear(); + } + return this; + } + + public String toString() { + return "Sql: " + sql + "\nPara: " + paraList; + } +} diff --git a/src/main/java/com/jfinal/kit/tpl/TplSource.java b/src/main/java/com/jfinal/kit/tpl/TplSource.java new file mode 100644 index 0000000..c05e5ff --- /dev/null +++ b/src/main/java/com/jfinal/kit/tpl/TplSource.java @@ -0,0 +1,29 @@ +package com.jfinal.kit.tpl; + +import com.jfinal.template.source.ISource; + +/** + * 封装 sql 模板源 + */ +public class TplSource { + + public String file; + public ISource source; + + public TplSource(String file) { + this.file = file; + this.source = null; + } + + public TplSource(ISource source) { + this.file = null; + this.source = source; + } + + public boolean isFile() { + return file != null; + } +} + + + diff --git a/src/main/resources/tpl.sql b/src/main/resources/tpl.sql new file mode 100644 index 0000000..6599e64 --- /dev/null +++ b/src/main/resources/tpl.sql @@ -0,0 +1,8 @@ +#tpl("findGirl") + select * from girl where age > ? and age < ? and weight < 50 +#end + +#tpl("findGirl2") + select * from girl where age > ? and age < ? and weight < 50 +#end + diff --git a/src/test/java/com/jfinal/template/TplKitTest.java b/src/test/java/com/jfinal/template/TplKitTest.java new file mode 100644 index 0000000..3710d6c --- /dev/null +++ b/src/test/java/com/jfinal/template/TplKitTest.java @@ -0,0 +1,28 @@ +package com.jfinal.template; + +import com.jfinal.kit.Kv; +import com.jfinal.kit.TplKit; +import org.junit.Test; + +import java.io.File; + +public class TplKitTest { + + @Test + public void run() { + TplKit tplKit = TplKit.use("", true); + + tplKit.addTplTemplate(new File("E:\\wk\\HaoGamePlatfProject\\conf"), x -> x.getName().endsWith(".tpl")); + + + tplKit.parseTplTemplate(); + + TplKit tplKit2 = TplKit.use("", true); + + String findGirl = tplKit2.getTpl("abx", Kv.by("a", 12321)); + + System.out.println(findGirl); + + + } +}