解析sql 添加条件

目的:

给sql每个待匹配的table_name,添加自己的条件

问题:

  1. 需要支持复杂的sql语句
  2. 需要支持小括号
  3. 需要支持自连接
  4. 需要支持单引号
  5. 暂不支持双引号

思路:

  1. 找 table_name,顺便找到table_name的别名
  2. 向后找同层次的where 表达式位置, 添加条件
  3. 找不到,则,找到where同层次应该在的位置,添加where以及条件

代码实现:

主方法,主流程

/**
     * 根据sql和info信息,生成新的sql
     * @param sql
     *  待处理sql
     * @param info
     *  数据处理信息
     * 	两项内容:1,table_name;2,不包含别名的sql条件
     * @return
     *  处理后的sql
     */
    private static String getDataSql(String sql, DataInfo info) {
        /*
         * 目的:
         * 给sql每个待匹配的table_name,添加自己的条件
         * 问题:
         * 1, 需要支持复杂的sql语句
         * 2, 需要支持小括号
         * 3, 需要支持自连接
         * 4, 需要支持单引号
         * 思路:
         * 1,找 table_name,顺便找到table_name的别名
         * 2,向后找同层次的where 表达式位置, 添加条件
         * 3,找不到,则,找到where同层次应该在的位置,添加where以及条件
         * 注意:
         * 暂不支持双引号!!!
         */
        // 判空
        if (StrUtil.isBlank(sql)
            || info == null
            || StrUtil.isBlank(info.getTableName())
            || StrUtil.isBlank(info.getSql())){
            return sql;
        }
        // where以及where后面的关键字,以及结尾
        Map<String, Integer> whereAfter = new HashMap<>();
        whereAfter.put("where", 0);
        whereAfter.put("group", 0);
        whereAfter.put("having", 0);
        whereAfter.put("order", 0);
        whereAfter.put("limit", 0);
        whereAfter.put("unit", 0);
        whereAfter.put(";", 0);
        // 初步处理, 转小写
        sql = toLowerCaseSql(sql);
        info.setTableName(info.getTableName().toLowerCase());
        info.setSql(toLowerCaseSql(info.getSql()));
        // 准备其他数据
        int beginIndex = 0;
        char[] sqlChars = sql.toCharArray();
        // 不止一个位置需要处理
        while (true){
            beginIndex = indexOfMy2(sql, info.getTableName(), beginIndex);
            if (beginIndex < 0) {
                break;
            }
            // 找到触发点后,顺便获取表名
            String tableName = findTableNameWord(sql,  beginIndex, info.getTableName());
            // 可能where位置的关键词
            for (Map.Entry<String, Integer> entry : whereAfter.entrySet()) {
                String key = entry.getKey();
                int of = indexOfMy(sql, key, beginIndex, 0, false);
                whereAfter.put(key, of > 0 ? of : sql.length());
            }
            // 右括号处理,ps:where一定在多余的右括号之前。
            whereAfter.put("rpIndex", sql.length());
            int rpn = 0;
            for (int i = beginIndex; i < sqlChars.length; i++) {
                if (sqlChars[i] == '('){
                    rpn --;
                }
                else if (sqlChars[i] == ')'){
                    rpn ++;
                }
                if (rpn > 0){
                    whereAfter.put("rpIndex", i);
                    break;
                }
            }
            // 拿到最靠前的位置,进行处理
            int afterIndex = whereAfter.values().stream().min(Integer::compareTo).orElse(sql.length());
            /*
             * 拿到位置之后,可能分为三种情况
             *  1,到结尾了
             *  2,找到了where语句
             *  3,没找到where语句,找到了where语句应该在的位置
             */
            if (afterIndex == sql.length()){
                sql = sql + " where " + tableName + "."+ info.getSql();
            }else if(sql.startsWith("where", afterIndex)){
                sql = sql.substring(0, afterIndex + 5) + " " + tableName + "." + info.getSql() + " and " + sql.substring(afterIndex + 5);
            }else {
                sql = sql.substring(0, afterIndex) + " where " + tableName + "." + info.getSql() + " " + sql.substring(afterIndex);
            }
            // beginIndex 向前移动
            beginIndex ++;
        }
        return sql;
    }

sql转小写处理

    /**
     * sql语句转小写
     * @param sql
     *  待处理sql
     * @return
     * 处理完的sql
     */
    private static String toLowerCaseSql(String sql) {
        if (StrUtil.isBlank(sql)){
            return null;
        }
        char[] chars = sql.toCharArray();
        boolean toLower = true;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '\''){
                toLower = !toLower;
            }
            if (toLower){
                if (chars[i] >= 'A' && chars[i] <= 'Z'){
                    chars[i] = (char) (chars[i] - ('A' - 'a'));
                }
            }
        }
        return String.valueOf(chars);
    }

sql内拿到表名

    /**
     * sql查找的时候不进行单引号内部都的扫描匹配
     * @param sql
     *  待处理sql
     * @param tableName
     *  表名
     * @param beginIndex
     *  开始位置
     * @return
     *
     */
    private static int indexOfMy2(String sql, String tableName, int beginIndex) {
        int of = sql.indexOf(tableName, beginIndex);
        int of1 = sql.indexOf("'");
        if (of < 0){
            return -1;
        }
        if (of1 < 0){
            return of;
        }
        // 去掉 table_name被引用的情况
        int off = sql.indexOf(tableName + ".", beginIndex);
        int offf = sql.indexOf(tableName + "\n.", beginIndex);
        if (of == off){
            return indexOfMy2(sql, tableName, ++ beginIndex);
        }
        if (of == offf){
            return indexOfMy2(sql, tableName, ++ beginIndex);
        }

        char[] chars = sql.toCharArray();
        boolean isLive = false;
        boolean isOk = false;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '\''){
                isLive = !isLive;
            }
            if (i == of){
                if (isLive){
                    isOk = true;
                }else {
                    return of;
                }
            }
            if (!isLive && isOk){
                return indexOfMy2(sql, tableName, ++i);
            }
        }
        return -1;
    }

获取表的别名,没有就拿表名

    /**
     * 拿到表名或者表的别名
     * @param sql
     *  sql语句
     * @param beginIndex
     *  表名点
     * @param tableName
     *  表名, 也是当前字符串起始位置
     * @return
     *  表的全名,或者别名
     */
    private static String findTableNameWord(String sql, int beginIndex, String tableName){
        /*
         * 目标:
         * 拿表的别名,没有则返回表名。
         * 思路:
         * 给了sql,以及表名的起始位置,以及表名
         * 基本去掉表名之后,看一下后面有什么就行,大概有几种情况:
         * 1,sql结束了,肯定没有别名
         * 2,sql没有结束,进行trim之后,肯定可以取到值的
         * 3,看看取值是否是关键字,不是就是别名,是的话,就没有别名
         * 4,其他值的处理:右括号,逗号等,符号处理。
         */
        String s = sql.substring(beginIndex).replaceFirst(tableName, "").trim();
        if (s.length() == 0) {
            return tableName;
        }
        char[] chars = s.toCharArray();
        StringBuilder nextWord = new StringBuilder();
        for (char aChar : chars) {
            if (aChar >= 'a' && aChar <= 'z'){
                nextWord.append(aChar);
            }else {
                break;
            }
        }
        String next = nextWord.toString();
        Set<String> set = new HashSet<>();
        set.add("inner");
        set.add("full");
        set.add("left");
        set.add("right");
        set.add("join");
        set.add("where");
        set.add("group");
        set.add("having");
        set.add("order");
        set.add("limit");
        set.add("unit");
        if (set.contains(next) || StrUtil.isBlank(nextWord)){
            return tableName;
        }
        return next;
    }

sql中拿到同级别关键字的位置


    /**
     * indexOf 添加更多处理
     * 解决小括号问题。
     * 解决单引号问题。
     * @param sql
     *  待处理sql
     * @param key
     *  待匹配值(String)
     * @param beginIndex
     *  开始位置
     * @param lpn
     *  已经匹配到的左括号数量
     * @param isLive
     *  是否已经匹配到单引号
     * @return
     *  最后匹配到的值
     */
    private static int indexOfMy(String sql, String key, int beginIndex, int lpn, boolean isLive) {
        /*
         * 目的:
         *  1,解决匹配sql的时候,不进行小括号中的匹配
         *  2,解决匹配sql的时候,不进行单引号中的匹配
         * 思路:
         *  1,截至条件:后面的sql找不到key,不用处理,返回-1即可
         *  2,小括号问题:向后扫描匹配括号,左括号+1,右括号-1,
         *      多出的左括号可能为下一层,此时不能判定处理,只能接着向后找。
         *      多出的右括号不需要处理,匹配括号的时候,已经单独拿出来处理。
         *      小括号初始值设置为0,表示,没有括号的意思
         *  3,单引号问题:向后赛马匹配单引号,有则置为相反的,因为相邻的单引号,一定会组成一对,进行抵消操作
         *      若是多出一个,则没有抵消的地方,则此时关键词可能在小括号中,不能进行匹配,只能向后继续查找,
         *      单引号初始值设置为false,表示没有单引号的意思
         *
         */
        // 这次匹配到的位置
        int indexOf = sql.indexOf(key, beginIndex);
        // 找不到的情况,不用特殊处理
        if (indexOf < 0){
            return -1;
        }
        // 小括号问题
        int rIndex = sql.indexOf("(", beginIndex);
        // 单引号问题
        int sIndex = sql.indexOf("'", beginIndex);
        // 若是一个条件成立则进入循环操作
        if (lpn > 0 || rIndex > 0 && rIndex < indexOf
            || isLive || sIndex > 0 && sIndex < indexOf
        ){
            char[] chars = sql.toCharArray();
            for (int i = beginIndex; i < indexOf; i++) {
                if (chars[i] == '(') {
                    lpn ++;
                }
                if (chars[i] == ')') {
                    lpn --;
                }
                if (chars[i] == '\'') {
                    isLive = !isLive;
                }
            }
            // 若是,有一个条件成立,则递归处理,即向后继续查找
            if (lpn > 0){
                return indexOfMy(sql, key, ++ indexOf, lpn, isLive);
            }
            if (isLive){
                return indexOfMy(sql, key, ++ indexOf, lpn, true);
            }
        }
        return indexOf;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
package com.hexiang.utils; /** * SQLUtils utils = new SQLUtils(User.class); utils.setWhereStr("", "id", "=", 100).setWhereStr("and", "name", " ", "is null").setWhereStr("and", "date", ">=", new Date()); utils.setOrderByStr("id", "desc").setOrderByStr("name", "asc"); System.out.println(utils.buildSelectSQL()); System.out.println(utils.buildCountSQL()); */ import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; public class SqlUtils { private String beanName; private String beanShortName; private Map propertyMap; private List conditionList; private List relationList; private Map orderByMap; public SqlUtils(Class instance) { this.setBeanName(instance.getSimpleName()); this.setBeanShortName(Character.toLowerCase(this.getBeanName() .charAt(0)) + ""); init(); } public SqlUtils() { init(); } void init(){ propertyMap = new LinkedHashMap(); conditionList = new LinkedList(); relationList = new LinkedList(); orderByMap = new LinkedHashMap(); } /** * 添加查询条件 * * @param relation * 关联 "and","or"等 * @param property * 查询的对象属性 * @param condition * 查询的条件,关系符 * @param value * 查询的值 */ public SqlUtils setWhereStr(String relation, String property, String condition, Object value) { if(value != null){ relationList.add(relation); propertyMap.put(property, value); conditionList.add(condition); } return this; } private String buildWhereStr() { StringBuffer buffer = new StringBuffer(); if (!propertyMap.isEmpty() && propertyMap.size() > 0) { buffer.append("WHERE 1 = 1 "); int index = 0; for (String property : propertyMap.keySet()) { if (property != null && !property.equals("")) { buffer.append(r

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值