Java解析SQL的基本思路

天天在写sql,一直很好奇sql到底咋解析的,于是随便整个小demo。

比如sql是:

String sql = "select name,score,sex from users where name = 'jack' and isdelete <> 1 ; ";

中间可能有1个或者多个空格,就想着怎么着也得有个分词的类,就叫做Tokenizer吧。

public class Tokenizer implements Iterator<String> {

    String[] tokens;
    int index = 0;

    public Tokenizer(String sql) throws BadSqlGrammarException {
        sql = sql.trim();
        if (!sql.endsWith(";")) {
            throw new BadSqlGrammarException("SQL未正确结束!");
        }
        //去除多余的空格
        sql = sql.replace(";", " ;").replaceAll("\\s+", " ");
        //分词
        tokens = sql.split(" ");
    }


    @Override
    public boolean hasNext() {
        return index < tokens.length;
    }

    @Override
    public String next() {
        return tokens[index++];
    }

}

这是一个名为Tokenizer的Java类,实现了Iterator接口。它用于将输入的SQL语句进行分词。

构造函数Tokenizer(String sql)接受一个SQL语句作为参数,并对其进行处理。首先,它去除了字符串两端的空格,并检查SQL语句是否以分号结尾。如果不是以分号结尾,则抛出一个BadSqlGrammarException异常。

接下来,它使用正则表达式替换将分号替换为空格加分号,并将多个连续的空格替换为单个空格。然后,它使用空格作为分隔符,将SQL语句分割成一个字符串数组tokens。

hasNext()方法用于判断是否还有下一个分词。它通过比较index和tokens数组的长度来确定是否还有剩余的分词。

next()方法用于返回下一个分词。它返回tokens数组中index位置的元素,并将index加1,以便指向下一个分词。

实现Iterator是因为需要去遍历维护的String数组,方便后面解析。

然后是parser类,里面聚合一个Tokenizer的引用。

public class Parser {

    Tokenizer tokenizer;
    DBCmd cmd;
    
    Map<String, DBCmd> cmdMap = new HashMap<>();
    public Parser(String sql) throws BadSqlGrammarException {
        //用查表法,代替一大堆的if else
        this.cmdMap.put("select", new SelectCmd());
        this.tokenizer = new Tokenizer(sql);

        //根据第一个sql关键字来确定是什么sql命令
        this.cmd = this.cmdMap.get(tokenizer.next());

        if (cmd == null)
            throw new BadSqlGrammarException("未识别的sql命令!");

    }


    public void query() throws BadSqlGrammarException {
        cmd.query(tokenizer);
    }

}

这是一个名为Parser的Java类,用于解析SQL语句并执行相应的数据库命令。

该类包含以下成员变量和方法:

  1. tokenizer:一个Tokenizer对象,用于将输入的SQL语句分解为多个标记(tokens)。

  2. cmd:一个DBCmd对象,表示要执行的数据库命令。

  3. cmdMap:一个HashMap,用于将SQL关键字与对应的DBCmd对象进行映射。

构造函数Parser(String sql)用于初始化Parser对象。它接受一个SQL语句作为参数,并根据第一个SQL关键字确定要执行的数据库命令。具体步骤如下:

  1. 创建一个HashMap对象cmdMap,并将"select"关键字与SelectCmd对象进行映射。

  2. 创建一个Tokenizer对象,并将输入的SQL语句作为参数传递给它。

  3. 使用Tokenizer的next()方法获取SQL语句的第一个关键字,并通过cmdMap获取对应的DBCmd对象。

  4. 如果cmd为null,表示未识别的SQL命令,抛出BadSqlGrammarException异常。

query()方法用于执行数据库查询操作。它调用cmd对象的query()方法,并将Tokenizer对象作为参数传递给它。

总之,Parser类通过查表法将SQL关键字与对应的数据库命令进行映射,实现了SQL语句的解析和执行。

DBCmd做成一个抽象类,不同的命令需要单独设计一个类,去继承他。DBCmd就弄一些通用的方法即可。

public abstract class DBCmd {

    public abstract void query(Tokenizer tokenizer) throws BadSqlGrammarException;

    protected String splitUntilEnd(Tokenizer tokenizer) throws BadSqlGrammarException {
        return splitUntil(tokenizer, ";");
    }


    protected String splitUntil(Tokenizer tokenizer, String until) throws BadSqlGrammarException {
        StringBuffer sb = new StringBuffer();

        boolean find = false;
        while (tokenizer.hasNext()) {
            String next = tokenizer.next();
            if (!next.equals(until)) {
                sb.append(next).append(" ");
                continue;
            } else {
                find = true;
                break;
            }
        }
        if (!find)
            throw new BadSqlGrammarException("语法不正确");
        return sb.toString();
    }
}

这段代码是一个Java类,名为DBCmd。它是一个抽象类,意味着不能直接实例化,只能被其他类继承。

该类有一个抽象方法query,接受一个Tokenizer对象作为参数,并且可能会抛出BadSqlGrammarException异常。这个方法在子类中需要被实现。

类中还有两个受保护的方法:splitUntilEnd和splitUntil。这两个方法都接受一个Tokenizer对象和一个字符串作为参数,并且可能会抛出BadSqlGrammarException异常。

splitUntilEnd方法调用了splitUntil方法,并传入了分隔符";"。splitUntilEnd方法的作用是将Tokenizer对象中的字符串逐个读取,直到遇到分隔符";"为止,并将读取的字符串拼接成一个新的字符串返回。

splitUntil方法是一个循环,它不断从Tokenizer对象中读取字符串,如果读取的字符串不等于给定的分隔符until,则将字符串拼接到StringBuffer对象sb中,并在每个字符串之间添加一个空格。如果读取的字符串等于分隔符until,则循环结束,并返回拼接好的字符串。

如果循环结束后仍然没有找到分隔符until,则抛出一个BadSqlGrammarException异常,表示语法不正确。

总的来说,这段代码定义了一个抽象类DBCmd,其中包含了一些用于处理SQL语句的方法。子类可以继承该类,并实现query方法来执行具体的SQL查询操作。

只测试一下select语法,所以只写一个select操作类。

public class SelectCmd extends DBCmd {

    @Override
    public void query(Tokenizer tokenizer) throws BadSqlGrammarException {
        String querys = splitUntil(tokenizer, "from");
        String tableName = tokenizer.next();
        String condition = null;
        if (tokenizer.hasNext() && tokenizer.next().equals("where")) {
            condition = splitUntilEnd(tokenizer);
        }
        System.out.println("查询字段:" + querys);
        System.out.println("查询表:" + tableName);
        System.out.println("查询条件:" + condition);

    }


}

最后测试一下子:

public static void main(String[]args)throws BadSqlGrammarException{
        String sql="select  name,score,sex from    users where name = 'jack' and isdelete <> 1   ;  ";
        Parser parser=new Parser(sql);
        parser.query();

}

效果:

查询字段:name,score,sex 查询表:users 查询条件:name = 'jack' and isdelete <> 1

猜你喜欢

转载自blog.csdn.net/weixin_39570751/article/details/131568510