第一步:先对sql语句进行预处理;
对于用户,我们应该接受各种形式的查询语句书写,单行或者多行,语句中单个空格或者多个空格的间隔等等。但是我们要解析sql语句,就首先要让对它们做标准化,这样才能进行我们下一步处理。系统中的处理要求:
1)消除SQL语句前后的空白,将其中的连续空白字符(包括空格,TAB和回车换行)替换成单个空格;
2)将sql语句全变成小写形式(或大写形式);
3)在SQL语句的尾后加上结束符号“ENDOFSQL”(原因后面解释)
例如:用户输入:“select c1,c2,c3 from t1,t2, t3 where condi1=5 and condi6=6 or condi7=7 order
by g1,g2”
通过预处理应该为:“select c1,c2,c3 from t1,t2,t3 where condi1=5 and condi6=6 or condi7=7 order by g1,g2”
第二步:将查询语句切分为语句块;
以查询语句为例(本文中主要是以查询语句作为例子讲解,其它删除,插入等语句原理于此类似,因为查询语句相对复杂,所以用来i讲解),正如上面我们标准化后的语句一样,我们要进行下一步的,对表中数据的处理,首先要知道是对那些表处理,要满足那些条件,输出那些属性,以什么顺序输出等。所以查询语句就可以分割为以下几个块:
1)select c1,c2,c3 from:属性输出块;块头(start)select,块尾(end)from,这个块我们关心的信息(body):c1,c2,c3;以下块类似分析
2)from….where; 涉及数据表块。
3)where…..order by; 查询条件块。
4)order by…..ENDOFSQL; 属性输出顺序。这里也就看出了我们为什么要在查询语句末尾加上结束符,是为了最后一个块的限定需要。
知道了如何分块,接下来要做的就是在我们已经标准化的sql语句上进行块的切割,这里我们用到了正则表达式,以第二个块from….where的查询为例,我们做个分析
“(from)(.+)( where | on | having | group by | order by | ENDOFSQL)“
以上就是第二个块的正则匹配式(其它块的匹配式下面也会给出),可以看出,在一个sql查询语句中,from块中跟from搭配出现的不只是where,还可以是on,having,group by等,那么通过这个正则式我们可以得到如下的块:
from …. where
from …. on
from …. having
from …. group by
from …. order by
from …. ENDOFSQL
这里我们要注意一点,就是在通过正则式对sql语句进行匹配时,我们不能对整个sql语句进行一次匹配操作,因为正则匹配是贪心匹配,它总是尽可能的向后查找,匹配到最大的语句段。就拿上述语句为例,如果通过对整个sql语句进行一次匹配,得到的就不是from….where这个语句段而是from …. where …. order by。显然这不是我们想要的。所以我们只能牺牲效率,通过对整个sql语句进行逐次递增的查询方式来查找相应的语句块。给出一个查询过程,加强理解,以上述sql语句为例,对第一个语句块的查找过程是
s
se
sel
………….
select c1,c2,c3 from
这样就找到第一个块,以此类推,找第二个块时候又从头逐次递增查找。
第三步:找到了各个块,我们还要把我们最关心的信息提取出来,就是夹在语句块头和尾之间的body部分,这个就好实现了,一般的sql语句中都会用逗号来做分割,我们提取出各个块的body信息。
步骤介绍完了,下面就上代码!
package com.hz.util.sql;
import java.util.List;
/**
* 单句sql解析器制造工厂
*
* @author Blossom
* @time 2016年9月9日
*/
public class SqlParserUtil {
/**
* 方法的主要入口
*
* @author Blossom
* @time 2016年9月9日
* @param sql
* 要解析的sql语句
* @return 返回解析结果
* @return_type String
*
*/
public String getParsedSql(String sql) {
sql = sql.trim();
sql = sql.toLowerCase();
sql = sql.replace("\\s{1,}", " ");
sql = "" + sql + " ENDOFSQL";
return SingleSqlParserFactory.generateParser(sql).getParsedSql();
}
/**
* SQL语句解析的接口
*
* @author Blossom
* @time 2016年9月9日
* @param sql
* 要解析的sql语句
* @return 返回解析结果
* @return_type List<SqlSegment>
*
*/
public List<SqlSegment> getParsedSqlList(String sql) {
sql = sql.trim();
sql = sql.trim();
sql = sql.toLowerCase();
sql = sql.replace("\\s{1,}", " ");
sql = "" + sql + " ENDOFSQL";
return SingleSqlParserFactory.generateParser(sql).returnSqlSegments();
}
}
<pre name="code" class="java">package com.hz.util.sql;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* sql语句片段
*
* @author Blossom
* @time 2016年9月9日
*/
public class SqlSegment {
private static final String Crlf = "@";
private static final String FourSpace = " ";
// sql语句片段开头
private String start;
// sql语句片段中间部分
private String body;
// sql语句片段结束部分
private String end;
// 表示片段的正则表达式
private String segmentRegExp;
// 分割后的body小片段
private List<String> bodyPieces;
// 用于分割中间部分的正则表达式
private String bodySplitPattern;
/**
* @author Blossom
* @time 2016年9月9日
*/
public SqlSegment(String segmentRegExp, String bodySplitPattern) {
start = "";
body = "";
end = "";
this.segmentRegExp = segmentRegExp;
this.bodySplitPattern = bodySplitPattern;
this.bodyPieces = new ArrayList<>();
}
/**
* 从sql中查找符合segmentRegExp的部分,并赋值到start,body,end三个属性中
*
* @author Blossom
* @time 2016年9月9日
* @param sql
* @return_type void
*
*/
public void parse(String sql) {
Pattern pattern = Pattern.compile(segmentRegExp, Pattern.CASE_INSENSITIVE);
for (int i = 0; i <= sql.length(); i++) {
String shortSql = sql.substring(0, i);
// 测试输出的子句是否正确 System.out.println(shortSql);
Matcher matcher = pattern.matcher(shortSql);
while (matcher.find()) {
start = matcher.group(1);
body = matcher.group(2);
end = matcher.group(3);
return;
}
}
}
/**
* 解析body片段
*
* @author Blossom
* @time 2016年9月9日
* @return_type void
*
*/
@SuppressWarnings("unused")
private void parseBody() {
List<String> ls = new ArrayList<String>();
Pattern p = Pattern.compile(bodySplitPattern, Pattern.CASE_INSENSITIVE);
// 先清除掉前后空格
body = body.trim();
Matcher m = p.matcher(body);
StringBuffer sb = new StringBuffer();
boolean result = m.find();
while (result) {
m.appendReplacement(sb, m.group(0) + Crlf);
result = m.find();
}
m.appendTail(sb);
// 再按空格断行
String[] arr = sb.toString().split(" ");
int arrLength = arr.length;
for (int i = 0; i < arrLength; i++) {
String temp = FourSpace + arr[i];
if (i != arrLength - 1) {
// temp=temp+Crlf; }
ls.add(temp);
}
bodyPieces = ls;
}
}
/**
* 取得解析好的sql片段
*
* @author Blossom
* @time 2016年9月9日
* @return
* @return_type String
*
*/
public String getParsedSqlSegment() {
StringBuffer sb = new StringBuffer();
sb.append(start + Crlf);
for (String piece : bodyPieces) {
sb.append(piece + Crlf);
}
return sb.toString();
}
/**
* @return the start
*/
public String getStart() {
return start;
}
/**
* @param start
* the start to set
*/
public void setStart(String start) {
this.start = start;
}
/**
* @return the body
*/
public String getBody() {
return body;
}
/**
* @param body
* the body to set
*/
public void setBody(String body) {
this.body = body;
}
/**
* @return the end
*/
public String getEnd() {
return end;
}
/**
* @param end
* the end to set
*/
public void setEnd(String end) {
this.end = end;
}
/**
* @return the segmentRegExp
*/
public String getSegmentRegExp() {
return segmentRegExp;
}
/**
* @param segmentRegExp
* the segmentRegExp to set
*/
public void setSegmentRegExp(String segmentRegExp) {
this.segmentRegExp = segmentRegExp;
}
/**
* @return the bodyPieces
*/
public List<String> getBodyPieces() {
return bodyPieces;
}
/**
* @param bodyPieces
* the bodyPieces to set
*/
public void setBodyPieces(List<String> bodyPieces) {
this.bodyPieces = bodyPieces;
}
/**
* @return the bodySplitPattern
*/
public String getBodySplitPattern() {
return bodySplitPattern;
}
/**
* @param bodySplitPattern
* the bodySplitPattern to set
*/
public void setBodySplitPattern(String bodySplitPattern) {
this.bodySplitPattern = bodySplitPattern;
}
}
package com.hz.util.sql;
import java.util.ArrayList;
import java.util.List;
/**
* 单句sql解析器,单句即非嵌套的意思
*
* @author Blossom
* @time 2016年9月9日
*/
public abstract class BaseSingleSqlParser {
// 原始sql语句
protected String originalSql;
// sql语句片段
protected List<SqlSegment> segments;
/**
* @author Blossom
* @time 2016年9月9日
*/
public BaseSingleSqlParser(String originalSql) {
this.originalSql = originalSql;
segments = new ArrayList<>();
initializeSegments();
splitSqlToSegment();
}
/**
* @author Blossom
* @time 2016年9月9日
* @return_type void
*
*/
protected void splitSqlToSegment() {
for (SqlSegment sqlSegment : segments) {
sqlSegment.parse(originalSql);
}
}
/**
* @author Blossom
* @time 2016年9月9日
* @return_type void
*
*/
protected abstract void initializeSegments();
/**
* 得到解析完毕的Sql语句
*
* @author Blossom
* @time 2016年9月9日
* @return
* @return_type String
*
*/
public String getParsedSql() {
StringBuffer stringBuffer = new StringBuffer();
for (SqlSegment sqlSegment : segments) {
stringBuffer.append(sqlSegment.getParsedSqlSegment());
}
String result = stringBuffer.toString().replaceAll("@+", "\n");
return result;
}
/**
* 得到解析的sql片段
*
* @author Blossom
* @time 2016年9月9日
* @return
* @return_type List<SqlSegment>
*
*/
public List<SqlSegment> returnSqlSegments() {
int segmentLength = this.segments.size();
if (segmentLength != 0) {
List<SqlSegment> result = this.segments;
return result;
}
return null;
}
}
package com.hz.util.sql;
/**
* 单句删除语句解析
*
* @author Blossom
* @time 2016年9月9日
*/
public class DeleteSqlParser extends BaseSingleSqlParser {
/**
* @author Blossom
* @time 2016年9月9日
* @param originalSql
*/
public DeleteSqlParser(String originalSql) {
super(originalSql);
}
/**
* @author Blossom
* @time 2016年9月9日
* @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments()
*/
@Override
protected void initializeSegments() {
segments.add(new SqlSegment("(delete from)(.+)(where | ENDOFSQL)", "[,]"));
segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)", "(and|or)"));
}
}
package com.hz.util.sql;
/**
* 单句查询插入语句解析器
*
* @author Blossom
* @time 2016年9月9日
*/
public class InsertSelectSqlParser extends BaseSingleSqlParser {
/**
* @author Blossom
* @time 2016年9月9日
* @param originalSql
*/
public InsertSelectSqlParser(String originalSql) {
super(originalSql);
}
/**
* @author Blossom
* @time 2016年9月9日
* @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments()
*/
@Override
protected void initializeSegments() {
segments.add(new SqlSegment("(insert into)(.+)( select )", "[,]"));
segments.add(new SqlSegment("(select)(.+)(from)", "[,]"));
segments.add(new SqlSegment("(from)(.+)( where | on | having | groups+by | orders+by | ENDOFSQL)",
"(,|s+lefts+joins+|s+rights+joins+|s+inners+joins+)"));
segments.add(new SqlSegment("(where|on|having)(.+)( groups+by | orders+by | ENDOFSQL)", "(and|or)"));
segments.add(new SqlSegment("(groups+by)(.+)( orders+by| ENDOFSQL)", "[,]"));
segments.add(new SqlSegment("(orders+by)(.+)( ENDOFSQL)", "[,]"));
}
}
package com.hz.util.sql;
/**
* 单句插入语句解析
*
* @author Blossom
* @time 2016年9月9日
*/
public class InsertSqlParser extends BaseSingleSqlParser {
/**
* @author Blossom
* @time 2016年9月9日
* @param originalSql
*/
public InsertSqlParser(String originalSql) {
super(originalSql);
}
/**
* @author Blossom
* @time 2016年9月9日
* @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments()
*/
@Override
protected void initializeSegments() {
segments.add(new SqlSegment("(insert into)(.+)([(])", "[,]"));
segments.add(new SqlSegment("([(])(.+)( [)] values )", "[,]"));
segments.add(new SqlSegment("([)] values [(])(.+)( [)])", "[,]"));
}
/**
* @author Blossom
* @time 2016年9月9日
* @return
* @see com.hz.util.sql.BaseSingleSqlParser#getParsedSql()
*/
@Override
public String getParsedSql() {
String result = super.getParsedSql();
result += ")";
return result;
}
}
package com.hz.util.sql;
/**
* 单句查询语句解析器
*
* @author Blossom
* @time 2016年9月9日
*/
public class SelectSqlParser extends BaseSingleSqlParser {
/**
* @author Blossom
* @time 2016年9月9日
* @param originalSql
*/
public SelectSqlParser(String originalSql) {
super(originalSql);
}
/**
* @author Blossom
* @time 2016年9月9日
* @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments()
*/
@Override
protected void initializeSegments() {
segments.add(new SqlSegment("(select)(.+)(from)", "[,]"));
segments.add(new SqlSegment("(from)(.+)( where | on | having | group by | order by | ENDOFSQL)",
"(,| left join | right join | inner join )"));
segments.add(new SqlSegment("(where|on|having)(.+)( group by | order by | ENDOFSQL)", "(and|or)"));
segments.add(new SqlSegment("(group by)(.+)( order by| ENDOFSQL)", "[,]"));
segments.add(new SqlSegment("(order by)(.+)( ENDOFSQL)", "[,]"));
}
}
package com.hz.util.sql;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Blossom
* @time 2016年9月9日
*/
public class SingleSqlParserFactory {
public static BaseSingleSqlParser generateParser(String sql) {
if (contains(sql, "(insert into)(.+)(select)(.+)(from)(.+)")) {
return new InsertSelectSqlParser(sql);
} else if (contains(sql, "(select)(.+)(from)(.+)")) {
return new SelectSqlParser(sql);
} else if (contains(sql, "(delete from)(.+)")) {
return new DeleteSqlParser(sql);
} else if (contains(sql, "(update)(.+)(set)(.+)")) {
return new UpdateSqlParser(sql);
} else if (contains(sql, "(insert into)(.+)(values)(.+)")) {
return new InsertSqlParser(sql);
}
return new InsertSqlParser(sql);
}
/**
* 看word是否在lineText中存在,支持正则表达式
*
* @author Blossom
* @time 2016年9月9日
* @param sql
* @param regExp
* @return
* @return_type boolean
*
*/
private static boolean contains(String sql, String regExp) {
Pattern pattern = Pattern.compile(regExp, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(sql);
return matcher.find();
}
}
package com.hz.util.sql;
/**
* 单句更新sql语句
*
* @author Blossom
* @time 2016年9月9日
*/
public class UpdateSqlParser extends BaseSingleSqlParser {
/**
* @author Blossom
* @time 2016年9月9日
* @param originalSql
*/
public UpdateSqlParser(String originalSql) {
super(originalSql);
}
/**
* @author Blossom
* @time 2016年9月9日
* @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments()
*/
@Override
protected void initializeSegments() {
segments.add(new SqlSegment("(update)(.+)(set)", "[,]"));
segments.add(new SqlSegment("(set)(.+)( where | ENDOFSQL)", "[,]"));
segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)", "(and|or)"));
}
}