分一个mysql拼接where语句的Directive,并请教一个问题


/**
 *
 * 拼接Where的指令,较简单的where组装,如果复杂的请在模版直接写where 语句
 * @author xianyl
 * @since 2018年1月26日-下午8:57:02
 */
public class WhereDirective extends Directive {    
    //运算符号集合、逻辑符号集合、between条件集合,字段名
    public static final String KV_SIGNS = "kv_signs";
    public static final String KV_LOGICS = "kv_logics";
    public static final String KV_BETWEEN = "kv_between";
    
    
    public void exec(Env env, Scope scope, Writer writer) {
        SqlPara sqlPara = (SqlPara)scope.get("_SQL_PARA_");//SqlKit.SQL_PARA_KEY 需要跟它一致
        if (sqlPara == null) {
            throw new TemplateException("#where directive invoked by getSqlPara(...) method only", location);
        }
        
        
        //获取参数
        Map<String, Object> data = null;
        Kv signs = null, logics = null, between = null;
        try {
            Record record = (Record)exprList.eval(scope);
            data = record.getColumns();
            signs = (Kv) data.remove(KV_SIGNS);
            logics = (Kv) data.remove(KV_LOGICS);
            between = (Kv) data.remove(KV_BETWEEN);            
        } catch (Exception e) {
        }
                
        //判断参数不能为null 并且只能是Record
        if(MapUtils.isEmpty(data)) return;
        StringBuilder sb = new StringBuilder();
        //循环拼接参数
        Object logic;
        Object sign;
        for(Entry<String, Object> d : data.entrySet()){
            Object _end = null;
            //判断逻辑符号
            if(logics == null){
                logic = LogicsSymbol.And;//没有逻辑符号集合,这是and
            }else{
                logic = logics.get(d.getKey());
                if(logic == null){
                    logic = LogicsSymbol.And;//没有对应字段的逻辑符号,这是and
                }
            }
            //判断运算符号
            if(signs == null){
                sign = SignsSymbol.Eq;//没有运算符号集合,这是eq
            }else{
                sign = signs.get(d.getKey());
                if(sign == null){
                    //没有对应字段的运算符号,这是eq
                    sign = SignsSymbol.Eq;
                }
                if(sign == SignsSymbol.Between){
                    //如果是between运算符号,需要检查between里面的field_end的数据取出
                    _end = between == null ? null : between.get(d.getKey() + "_end");
                }
            }
            
            //拼接条件
            this.join(sb,sqlPara,d.getKey(),d.getValue(),logic,sign,_end);
            
        }
        //写where
        write(writer, sb.toString());
    }

    /**
     * 拼接其中一个条件语句
     * @param sb  
     * @param sqlPara
     * @param key  字段名
     * @param value 字段值
     * @param logic 逻辑符号
     * @param sign 运算符号
     * @param _end between时字段的第二个值
     */
    private void join(StringBuilder sb, SqlPara sqlPara, String key, Object value, Object logic, Object sign, Object _end) {
        //第一时间判断_end 是否为null, 如果是 SignsSymbol.Between 转成 SignsSymbol.Gte
        if(sign == SignsSymbol.Between && _end == null){
            sign = SignsSymbol.Gte;
        }
        
        //拼接逻辑符号
        if(sb.length() == 0){
            if(logic == LogicsSymbol.And){
                sb.append(" where ");
            }else{
                sb.append(" where 1=1 ").append(logic);
            }
        }else{
            sb.append(" ").append(logic);            
        }
        
        //拼接参数
        sb.append(" ").append(key).append(" ").append(sign).append(" ").append("?");
        sqlPara.addPara(sign == SignsSymbol.Like ? "%"+value+"%" :value);
        if(sign == SignsSymbol.Between){
            //如果between
            sb.append(" and ").append("?");
            sqlPara.addPara(_end);
        }
    }
    

}

模版 :
#namespace("user")
  #sql("list")
      select * from user #where(data)
  #end
#end


java调用方法:
/**
     * 根据模板动态生成 sql 跟参数 
     * @param key  模板名 namespace.sql
     * @param record 查询条件 键值对
     * @param signs 运算符号集合  {"field":SignsSymbol.Gt} , 如果没有,则为Eq
     * @param logics 逻辑符号集合 {"field":LogicsSymbol.And}, 如果没有,则为Or
     * @param between between条件集合 {"field_end":value}, signs为between时检查这个,如果没有指定的field_end,则转为Gte运算符号
     * @return
     */
    public SqlPara getSqlPara(String key, Record data, Kv signs, Kv logics, Kv between){
        //这几个字段名需要跟WhereDirective里面的对应起来
        data.set(WhereDirective.KV_SIGNS, signs);
        data.set(WhereDirective.KV_LOGICS, logics);
        data.set(WhereDirective.KV_BETWEEN, between);
        //data跟sql模版的参数对应起来
        return Db.getSqlPara(key, Kv.by("data", data));
    }




评论区

sinwinglok

2018-01-29 14:41

/**
* 指令被解析时注入指令参数表达式列表,继承类可以通过覆盖此方法对参数长度和参数类型进行校验
*/
public void setExprList(ExprList exprList) {
this.exprList = exprList;
}



请问这个方法的使用场景是什么?它只有项目启动的时候才会触发一次啊。

JFinal

2018-01-29 15:44

@sinwinglok 触发场景是你可以对参数的长度、类型进行控制,看一下 com.jfinal.template.ext.directive 中的例子就知道了

热门反馈

扫码入社