Java 实现对Sql语句解析
来源:互联网 发布:网络兼职公司 编辑:程序博客网 时间:2024/05/22 18:37
最近要实现一个简易的数据库系统,除了要考虑如何高效的存储和访问数据,建立表关系外,对基本的sql查询语句要做一个解析,这样我们才能知道用户的查询要求;因为时间关系,参考了已有的一篇文章,并对其实现中出的小问题给予更正,在这里跟大家共享一下。原文请查阅http://www.cnblogs.com/pelephone/articles/sql-parse-single-word.html
第一步:先对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
sele
selec
select
select
select c
select c1
select c1,
select c1,c
select c1,c2
select c1,c2,
select c1,c2,c
select c1,c2,c3
select c1,c2,c3
select c1,c2,c3 f
select c1,c2,c3 fr
select c1,c2,c3 fro
select c1,c2,c3 from
这样就找到第一个块,以此类推,找第二个块时候又从头逐次递增查找。
第三步:找到了各个块,我们还要把我们最关心的信息提取出来,就是夹在语句块头和尾之间的body部分,这个就好实现了,一般的sql语句中都会用逗号来做分割,我们提取出各个块的body信息。
步骤介绍完了,下面就上代码,享乐吧...少年!
package com.sitinspring.common.sqlparser.single;import java.util.List;/** *//*** 单句Sql解析器制造工厂* @author 赵朝峰** @since 2013-6-10* @version 1.00*/public class SqlParserUtil{ /** *//** * 方法的主要入口 * @param sql:要解析的sql语句 * @return 返回解析结果 */ public String getParsedSql(String sql){ sql=sql.trim(); sql=sql.toLowerCase(); sql=sql.replaceAll("\\s{1,}", " "); sql=""+sql+" ENDOFSQL"; //System.out.println(sql); return SingleSqlParserFactory.generateParser(sql).getParsedSql(); } /** *//** * SQL语句解析的接口 * @param sql:要解析的sql语句 * @return 返回解析结果 */ public List<SqlSegment> getParsedSqlList(String sql) { sql=sql.trim(); sql=sql.toLowerCase(); sql=sql.replaceAll("\\s{1,}", " "); sql=""+sql+" ENDOFSQL"; //System.out.println(sql); return SingleSqlParserFactory.generateParser(sql).RetrunSqlSegments(); } }
package com.sitinspring.common.sqlparser.single;//import com.sitinspring.common.sqlparser.single.NoSqlParserException;import java.util.ArrayList;import java.util.List;import com.sitinspring.common.sqlparser.single.SqlSegment;/** *//*** 单句Sql解析器,单句即非嵌套的意思* @author 赵朝峰()** @since 2013-6-10* @version 1.00*/public abstract class BaseSingleSqlParser{/** *//** * 原始Sql语句 */protected String originalSql;/** *//** * Sql语句片段 */protected List<SqlSegment> segments;/** *//** * 构造函数,传入原始Sql语句,进行劈分。 * @param originalSql */public BaseSingleSqlParser(String originalSql){ this.originalSql=originalSql; segments=new ArrayList<SqlSegment>(); initializeSegments(); splitSql2Segment();}/** *//** * 初始化segments,强制子类实现 * */protected abstract void initializeSegments();/** *//** * 将originalSql劈分成一个个片段 * */protected void splitSql2Segment() { for(SqlSegment sqlSegment:segments) { sqlSegment.parse(originalSql); }}/** *//** * 得到解析完毕的Sql语句 * @return */public String getParsedSql() { //测试输出各个片段的信息 /* for(SqlSegment sqlSegment:segments) { String start=sqlSegment.getStart(); String end=sqlSegment.getEnd(); System.out.println(start); System.out.println(end); } */ StringBuffer sb=new StringBuffer(); for(SqlSegment sqlSegment:segments) { sb.append(sqlSegment.getParsedSqlSegment()); } String retval=sb.toString().replaceAll("@+", "\n"); return retval;}/** *//*** 得到解析的Sql片段* @return*/public List<SqlSegment> RetrunSqlSegments(){ int SegmentLength=this.segments.size(); if(SegmentLength!=0) { List<SqlSegment> result=this.segments; return result; } else { //throw new Exception(); return null; }}}
package com.sitinspring.common.sqlparser.single;import com.sitinspring.common.sqlparser.single.SqlSegment;/** *//**** 单句删除语句解析器* @author 赵朝峰** @since 2013-6-10* @version 1.00*/public class DeleteSqlParser extends BaseSingleSqlParser{public DeleteSqlParser(String originalSql) { super(originalSql);}@Overrideprotected void initializeSegments() { segments.add(new SqlSegment("(delete from)(.+)( where | ENDOFSQL)","[,]")); segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)","(and|or)"));}}
package com.sitinspring.common.sqlparser.single;import com.sitinspring.common.sqlparser.single.SqlSegment;/** *//**** 单句查询插入语句解析器* @author 赵朝峰** @since 2013-6-10* @version 1.00*/public class InsertSelectSqlParser extends BaseSingleSqlParser{public InsertSelectSqlParser(String originalSql) { super(originalSql);}@Overrideprotected 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.sitinspring.common.sqlparser.single;import com.sitinspring.common.sqlparser.single.SqlSegment;/** *//**** 单句插入语句解析器* @author 赵朝峰** @since 2013-6-10* @version 1.00*/public class InsertSqlParser extends BaseSingleSqlParser{public InsertSqlParser(String originalSql) { super(originalSql);}@Overrideprotected void initializeSegments() { segments.add(new SqlSegment("(insert into)(.+)([(])","[,]")); segments.add(new SqlSegment("([(])(.+)( [)] values )","[,]")); segments.add(new SqlSegment("([)] values [(])(.+)( [)])","[,]"));}@Overridepublic String getParsedSql() { String retval=super.getParsedSql(); retval=retval+")"; return retval;}}复制代码复制代码package com.sitinspring.common.sqlparser.single;public class NoSqlParserException extends Exception{ private static final long serialVersionUID = 1L; NoSqlParserException() { } NoSqlParserException(String sql) { //调用父类方法 super(sql); }}
package com.sitinspring.common.sqlparser.single;import com.sitinspring.common.sqlparser.single.SqlSegment;/** *//**** 单句查询语句解析器* @author 赵朝峰** @since 2013-6-10* @version 1.00*/public class SelectSqlParser extends BaseSingleSqlParser{public SelectSqlParser(String originalSql) { super(originalSql);}@Overrideprotected 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.sitinspring.common.sqlparser.single;import java.util.regex.Matcher;import java.util.regex.Pattern;//import com.sitinspring.common.sqlparser.single.NoSqlParserException;/** *//*** 单句Sql解析器制造工厂* @author 赵朝峰** @since 2013-6-10* @version 1.00*/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); } //sql=sql.replaceAll("ENDSQL", ""); else return new InsertSqlParser(sql); //throw new NoSqlParserException(sql.replaceAll("ENDOFSQL", ""));//对异常的抛出}/** *//** * 看word是否在lineText中存在,支持正则表达式 * @param sql:要解析的sql语句 * @param regExp:正则表达式 * @return */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.sitinspring.common.sqlparser.single;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;/** *//*** Sql语句片段** @author 赵朝峰** @since 2013-6-10* @version 1.00*/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 bodySplitPattern;/** *//** * 表示片段的正则表达式 */private String segmentRegExp;/** *//** * 分割后的Body小片段 */private List<String> bodyPieces;/** *//** * 构造函数 * @param segmentRegExp 表示这个Sql片段的正则表达式 * @param bodySplitPattern 用于分割body的正则表达式 */public SqlSegment(String segmentRegExp,String bodySplitPattern){ start=""; body=""; end=""; this.segmentRegExp=segmentRegExp; this.bodySplitPattern=bodySplitPattern; this.bodyPieces=new ArrayList<String>(); }/** *//** * 从sql中查找符合segmentRegExp的部分,并赋值到start,body,end等三个属性中 * @param sql */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); //测试body部分 //System.out.println(body); end=matcher.group(3); //测试相应的end部分 //System.out.println(end); parseBody(); return; } }}/** *//** * 解析body部分 * */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片段 * @return */public String getParsedSqlSegment(){ StringBuffer sb=new StringBuffer(); sb.append(start+Crlf); for(String piece:bodyPieces) { sb.append(piece+Crlf); } return sb.toString();}public String getBody(){ return body;}public void setBody(String body){ this.body=body;}public String getEnd(){ return end;}public void setEnd(String end){ this.end=end;}public String getStart(){ return start;}public void setStart(String start) { this.start=start;}}
package com.sitinspring.common.sqlparser.single;import com.sitinspring.common.sqlparser.single.SqlSegment;/** *//**** 单句更新语句解析器* @author 赵朝峰** @since 2013-6-10* @version 1.00*/public class UpdateSqlParser extends BaseSingleSqlParser{public UpdateSqlParser(String originalSql) { super(originalSql);}@Overrideprotected void initializeSegments() { segments.add(new SqlSegment("(update)(.+)(set)","[,]")); segments.add(new SqlSegment("(set)(.+)( where | ENDOFSQL)","[,]")); segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)","(and|or)"));}}执行结果:自己写了个测试的类
import java.util.List;import com.sitinspring.common.sqlparser.single.*;public class Test { /** *//** * 单句Sql解析器制造工厂 * @author 赵朝峰 * * @since 2013-6-10 * @version 1.00 */ public static void main(String[] args) { // TODO Auto-generated method stub //String test="select a from b " + // "\n"+"where a=b"; //test=test.replaceAll("\\s{1,}", " "); //System.out.println(test); //程序的入口 String testSql="select c1,c2,c3 from t1,t2 where condi3=3 "+"\n"+" or condi4=5 order by o1,o2"; SqlParserUtil test=new SqlParserUtil(); String result=test.getParsedSql(testSql); System.out.println(result); //List<SqlSegment> result=test.getParsedSqlList(testSql);//保存解析结果 }}
结果为select c1, c2, c3from t1, t2where condi3=3 or condi4=5order by o1, o2
- Java 实现对Sql语句解析
- sql语句解析实现
- 通过java反射实现对javabean生成各种sql语句
- Oracle对Sql语句的软解析和硬解析?
- java实现sql语句批处理
- java实现对url解析
- 对sql查询语句组合查询的通用实现算法(c++版,java版)
- java代码实现oracle sql语句转mysql sql语句
- SQL 语句组装类 (JAVA 实现)
- java 反射实现快速sql语句
- 对数据库的备份和恢复操作,Sql语句实现
- SQL语句-实现对数据中两张表的访问
- Java实现对http协议的解析
- SQL语句解析
- SQL语句全解析
- SQL语句全解析
- SQL语句解析
- SQL语句解析
- Softmax的求导
- XMind思维导图是什么
- mysql查询今天、昨天、7天、近30天、本月、上一月 数据
- 轮播图+XListview
- xampp3.2.2配置多个监听端口和不同的网站目录
- Java 实现对Sql语句解析
- org.apache.catalina.startup.Catalina stopServer SEVERE: Could not contact localhost:8005. Tomcat may
- MQTT协议学习笔记(转载)
- 《辛雷学习方法》读书笔记——第三章 身体
- MD5 标准C++实现
- 全排列算法的java实现
- 完整版百度地图点击列表定位到对应位置并有交互动画效果demo
- 图换文字效果:任意标签,均可插入图片
- promise用法详解(写的很好)