天天在写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语句并执行相应的数据库命令。
该类包含以下成员变量和方法:
-
tokenizer:一个Tokenizer对象,用于将输入的SQL语句分解为多个标记(tokens)。
-
cmd:一个DBCmd对象,表示要执行的数据库命令。
-
cmdMap:一个HashMap,用于将SQL关键字与对应的DBCmd对象进行映射。
构造函数Parser(String sql)用于初始化Parser对象。它接受一个SQL语句作为参数,并根据第一个SQL关键字确定要执行的数据库命令。具体步骤如下:
-
创建一个HashMap对象cmdMap,并将"select"关键字与SelectCmd对象进行映射。
-
创建一个Tokenizer对象,并将输入的SQL语句作为参数传递给它。
-
使用Tokenizer的next()方法获取SQL语句的第一个关键字,并通过cmdMap获取对应的DBCmd对象。
-
如果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();
}
效果:文章来源:https://www.toymoban.com/news/detail-528427.html
查询字段:name,score,sex 查询表:users 查询条件:name = 'jack' and isdelete <> 1文章来源地址https://www.toymoban.com/news/detail-528427.html
到了这里,关于Java解析SQL的基本思路的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!