自定义指令实现sql语句中对in的问号拼接

测试效果:

        String sqlTemplate = "select * from t_gril where sex = #para(sex) and age in (#paraIn(age)) and name = #para(name)";
         Kv cond = Kv.create()
                 .set("sex", 1)
                 .set("name", "dddd")
                 .set("age", "18,19");
         SqlPara sp = Db.getSqlPara(sqlTemplate, cond);
//         String sqlTemplate = "select * from t_gril where sex = #para(0) and age in (#paraIn(1)) and name = #para(2)";
//         SqlPara sp = getSqlPara(sqlTemplate, 1,"18,19","ddde");
         log.debug(sp.getSql());
         log.debug(sp.getPara()[3]+"");

结果输出:

DEBUG - select * from t_gril where sex = ? and age in (?,?) and name = ?
DEBUG - dddd


原码

public class ParaInDirective extends Directive {

    /**
     * 描述:
     */
    private int index = -1;

    /**
     * 描述:
     * 
     * @author Robin Zhang
     * @created 2017年7月7日 下午2:57:23
     * @param exprList
     * @see com.jfinal.template.Directive#setExprList(com.jfinal.template.expr.ast.ExprList)
     */
    public void setExprList(ExprList exprList) {
        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);
                }
            }
        }
        this.exprList = exprList;
    }

    /**
     * 描述:
     * 
     * @author Robin Zhang
     * @created 2017年7月7日 下午2:57:31
     * @param env
     * @param scope
     * @param writer
     * @see com.jfinal.template.stat.ast.Stat#exec(com.jfinal.template.Env,
     *      com.jfinal.template.stat.Scope, java.io.Writer)
     */
    public void exec(Env env, Scope scope, Writer writer) {
        SqlPara sqlPara = (SqlPara) scope.get("_SQL_PARA_");
        if (sqlPara == null) {
            throw new TemplateException("#paraIn invoked by getSqlPara(...) method only", location);
        }
        StringBuffer inPlaceholder = new StringBuffer();
        String valueStr = "";
        if (index == -1) {
            Expr[] exprArray = exprList.getExprArray();
            Object ret = null;
            for (Expr expr : exprArray) {
                boolean hasPara = scope.getData().containsKey(expr.toString());
                if (hasPara) {
                    ret = expr.eval(scope);
                    if (ret==null) {//in类的参数值不可为空
                        throw new TemplateException(
                                "The value of parameter '"+expr.toString()+"' must not be null",
                                location);
                    }
                } else {//没有传参数抛异常
                    throw new TemplateException(
                            "The parameter '"+expr.toString()+"' must be define",
                            location);
                }
            }
            valueStr = String.valueOf(ret);
//            sqlPara.addPara(exprList.eval(scope));
        } else {
            Object[] paras = (Object[]) scope.get("_PARA_ARRAY_");
            if (paras == null) {
                throw new TemplateException(
                        "The #paraIn(" + index + ") directive must invoked by getSqlPara(String, Object...) method",
                        location);
            }
            if (index >= paras.length) {
                throw new TemplateException("The index of #paraIn directive is out of bounds: " + index, location);
            }
            valueStr  = String.valueOf(paras[index]);
//            sqlPara.addPara(paras[index]);
        }
        Object[] retArrray = valueStr.split(",");
        for (int i = 0; i < retArrray.length; i++) {
            inPlaceholder.append("?");
            if (i != retArrray.length - 1) {
                inPlaceholder.append(",");
            }
            sqlPara.addPara(retArrray[i]);
        }
        write(writer, inPlaceholder.toString());
    }
}

注册paraIn指令:

Engine.addDirective("paraIn", new ParaInDirective());

评论区

JFinal

2017-08-26 15:50

指令扩展又玩出了新意,超赞

JFinal

2017-08-26 15:59

看到你的扩展指令中需要使用 SqlKit.SQL_PARA_KEY 这类常量,jfinal 3.3 考虑将这些常量弄成 public 的,便于这类扩展

i++

2017-08-26 16:31

@JFinal 其实我自己不是用SqlKit.SQL_PARA_KEY 我自己用自己的常量了。只是发出来时改成这个了。没注意看这个不是public的。我改下

北流家园网

2017-08-27 09:29

@JFinal 3.3建议加个动态URL的指令

JFinal

2017-08-27 14:22

@北流家园网 具体需求是什么样的? 知道了需求才知道怎么添加这个 url 指令

北流家园网

2017-08-28 16:34

@JFinal 可以写个扩展指令 #renderUrl ,大致如下:
1:创建 RenderUrlDirective extends Directive
2:内部用一个 String content = HttpKit.get(url) 先获取url 指向的数据
3: ret = engine.getTemplateByString(content).renderToString() 得到渲染结果
4:通过 writer.write(ret) 将结果输出即可

JFinal

2017-08-28 16:45

@北流家园网 这个扩展为啥还需要 getTemplateByString(...) 这一步呢? 直接 writer.write(content) 不就可以了?

要知道从一个 url 过来的 content 之中如果有恶意代码,在使用模板引擎渲染的时候有危险性,当然这个危险性极小,因为 jfinal 模板引擎默认对很多操作已经做了屏蔽

北流家园网

2017-08-28 17:14

@JFinal 这4点是你之前回复我的,但我还是不懂如何写。

似水流言1

2017-10-19 15:27

这个第一个例子中getSqlPara()方法是在哪里,麻烦贴完整

青莲剑歌

2018-12-27 10:25

getSqlPara()第一个参数可以直接写sql模板的语句么,而不是.sql文件的key,我像你这么写怎么报空指针??

i++

2018-12-28 09:15

@青莲剑歌 就是sql模板的语句

i++

2018-12-28 09:20

@似水流言1 getSqlPara()方法在jfinal的Db类里啊

enderjo

2019-11-13 10:14

好用,谢谢分享。
自己改了下,把括号放ParaIn里面生成,用文档中方式更顺畅了。
#if(c.key.contains(" in "))
#(c.key) #paraIn(c.value)
#else
#(c.key) #para(c.value)
#end