Hibernate版本5.1.11FInal

以一句update语句作为例子。

update com.tydhot.eninty.User set userName=:userName where userId=:userId

上面这句hql经过antlr的语法解析之后,得到的语法树如下。

\-[50] Node: 'update'+-[22] Node: 'FROM'|  \-[90] Node: 'RANGE'|     \-[15] Node: '.'|        +-[15] Node: '.'|        |  +-[15] Node: '.'|        |  |  +-[108] Node: 'com'|        |  |  \-[108] Node: 'tydhot'|        |  \-[108] Node: 'eninty'|        \-[108] Node: 'User'+-[46] Node: 'set'|  \-[105] Node: '='|     +-[108] Node: 'userName'|     \-[127] Node: ':'|        \-[108] Node: 'userName'\-[52] Node: 'where'\-[105] Node: '='+-[108] Node: 'userId'\-[127] Node: ':'\-[108] Node: 'userId'

在antlr的语法树中,使用BaseAST作为一个语法树节点的抽象,在BaseAST存储中,其数据结构更加类似一个链表。

protected BaseAST down;
protected BaseAST right;

BaseAST中down代表其在树结构中的子节点,而right则代表跟他从属一个父节点的兄弟节点。在语法树中,父节点的down只指向逻辑顺序的第一个子节点。

承接上文,当QueryTranslatorImpl中调用parse()方法中,当语法解析器parser调用statement正式开始语法解析。

从语法文件hql.g中可以看到,所有hql解析的起点是statement规则。

statement: ( updateStatement | deleteStatement | selectStatement | insertStatement ) (EOF!);

从这里可以看到,语法解析器会依次从下面四个规则一次尝试去选择匹配,四个规则也正好对应了数据库操作的增删改查。

这里再次放上等待作为例子等待解析的hql语句。

update com.tydhot.eninty.User set userName=:userName where userId=:userId

我们可以看到,首先会尝试去选择匹配updateStatement规则,如代码所示。

try {      // for error handling{switch ( LA(1)) {case UPDATE:{updateStatement();astFactory.addASTChild(currentAST, returnAST);break;}case DELETE:{deleteStatement();astFactory.addASTChild(currentAST, returnAST);break;}case EOF:case FROM:case GROUP:case ORDER:case SELECT:case WHERE:{selectStatement();astFactory.addASTChild(currentAST, returnAST);break;}case INSERT:{insertStatement();astFactory.addASTChild(currentAST, returnAST);break;}default:{throw new NoViableAltException(LT(1), getFilename());}}

在上方的代码中,会从上一篇文章所提到的词法解析器去提取所等到解析的hql语句的第一个词,在这里,如果是update关键字开头,自然会开始进入updateStatement规则的匹配。

updateStatement: UPDATE^ (optionalVersioned)?optionalFromTokenFromClausesetClause(whereClause)?;

从语法文件的配置还是可以看出,只有在第一个在字符匹配到了update才会继续进行下面的匹配。接下来是根据这个语法规则antlr编译的代码。

match(UPDATE);
{
switch ( LA(1)) {
case VERSIONED:
{AST tmp2_AST = null;tmp2_AST = astFactory.create(LT(1));astFactory.addASTChild(currentAST, tmp2_AST);match(VERSIONED);break;
}
case FROM:
case IDENT:
{break;
}
default:
{throw new NoViableAltException(LT(1), getFilename());
}
}
}
optionalFromTokenFromClause();
astFactory.addASTChild(currentAST, returnAST);
setClause();
astFactory.addASTChild(currentAST, returnAST);
{
switch ( LA(1)) {
case WHERE:
{whereClause();astFactory.addASTChild(currentAST, returnAST);break;
}
case EOF:
{break;
}
default:
{throw new NoViableAltException(LT(1), getFilename());
}
}
}
updateStatement_AST = (AST)currentAST.root;

可以看到,在取出第一个词之后会通过match()确认关键字update的确认无误,并生成第一个update节点作为当前树的根节点,之后判断下一个词是否匹配版本关键字,对应语法文件的optionalVersion,当然根据语法文件中的正则显示该语句块可加可不加,那么自然代码中对于没有匹配到这一关键字,也没有什么别的操作。

而对于我们的例子语句

update com.tydhot.eninty.User set userName=:userName where userId=:userId

自然也没有影响,会继续往下匹配下一个语法规则,下一步则会匹配optionalFromTokenClause规则,代码中也会进入optionalFromTokenClause()代码段。

optionalFromTokenFromClause!: (FROM!)? f:path (a:asAlias)? {AST #range = #([RANGE, "RANGE"], #f, #a);#optionalFromTokenFromClause = #([FROM, "FROM"], #range);};

optionalFromTokenClause的语法规则就显得稍微复杂。编译后的代码如下。

switch ( LA(1)) {
case FROM:
{match(FROM);break;
}
case IDENT:
{break;
}
default:
{throw new NoViableAltException(LT(1), getFilename());
}
}
}
path();
f_AST = (AST)returnAST;
{
switch ( LA(1)) {
case AS:
case IDENT:
{asAlias();a_AST = (AST)returnAST;break;
}
case EOF:
case SET:
case WHERE:
{break;
}
default:
{throw new NoViableAltException(LT(1), getFilename());
}
}
}
optionalFromTokenFromClause_AST = (AST)currentAST.root;AST range = (AST)astFactory.make( (new ASTArray(3)).add(astFactory.create(RANGE,"RANGE")).add(f_AST).add(a_AST));optionalFromTokenFromClause_AST = (AST)astFactory.make( (new ASTArray(2)).add(astFactory.create(FROM,"FROM")).add(range));currentAST.root = optionalFromTokenFromClause_AST;
currentAST.child = optionalFromTokenFromClause_AST!=null &&optionalFromTokenFromClause_AST.getFirstChild()!=null ?optionalFromTokenFromClause_AST.getFirstChild() : optionalFromTokenFromClause_AST;
currentAST.advanceChildToEnd();

代码比较长,根据语法规则,首先会尝试匹配from关键字,当然无论有没有匹配到都不会影响下面的操作。

接下来会对下一个部分强制进行path规则的匹配,也会进入代码的path()方法。

path: identifier ( DOT^ { weakKeywords(); } identifier )*
;

Path的规则就显得很简单,首先强制要求必须存在一个常量,也就是说update语句的操作表对象至少要存在一个,但是在hql语句中可能存在以一个类名作为对象,就像下面这个例子。

update com.tydhot.eninty.User set userName=:userName where userId=:userId

那么根据语法规则可以匹配若干个点‘ .’起头的关键字进行匹配,下面是path()的代码。

identifier();
astFactory.addASTChild(currentAST, returnAST);
{
_loop315:
do {if ((LA(1)==DOT)) {AST tmp10_AST = null;tmp10_AST = astFactory.create(LT(1));astFactory.makeASTRoot(currentAST, tmp10_AST);match(DOT);weakKeywords();identifier();astFactory.addASTChild(currentAST, returnAST);}else {break _loop315;}} while (true);
}
path_AST = (AST)currentAST.root;

在path()中,会强制第一个匹配一个常量,匹配到则会生成一个语法树节点,之后会尝试在循环中不断去匹配点‘ .’,如果匹配到点,也会生成一个相应的语法树节点,并将这个节点作为当前树的根节点,其down则会指向刚才生成的常量节点,之后会继续匹配下一个常量,并作为第一个常量节点right所指向的节点。再下一次循环中,如果继续匹配到了点,则生成一个相应的新节点作为当前树的根节点,其down指向第一个循环所产生的点对应的树节点(也就是根节点),以此不断循环产生节点,达到一次中序遍历可以换元整个结果的目的,之前例子该部分生成的语法树如下。

\-[15] Node: '.'|        +-[15] Node: '.'|        |  +-[15] Node: '.'|        |  |  +-[108] Node: 'com'|        |  |  \-[108] Node: 'tydhot'|        |  \-[108] Node: 'eninty'|        \-[108] Node: 'User'

从最左边的com节点开始进行一次先序遍历就可以还原整个结果。

在path()部分得到的这部分结果将会回到optionalFromTokenClause规则当中继续作为语法树的成员操作。

之后如果匹配到了AS关键字或者空格一个常量,那么会继续进入asAlias的规则匹配,但是根据语法规则,此处也是可有可无的部分,给出的例子即使没有匹配到,也不会出现语法错误。

ST range = (AST)astFactory.make( (new ASTArray(3)).add(astFactory.create(RANGE,"RANGE")).add(f_AST).add(a_AST));
optionalFromTokenFromClause_AST = (AST)astFactory.make( (new ASTArray(2)).add(astFactory.create(FROM,"FROM")).add(range));

根据语法规则,产生的对象和其别名将会作为RANGE节点的子节点存放在语法树中,而会继续产生一个FROM节点继续作为RANGE的父节点,最后在optionalFromTokenClause规则中产生的树如下。

 +-[22] Node: 'FROM'|  \-[90] Node: 'RANGE'|     \-[15] Node: '.'|        +-[15] Node: '.'|        |  +-[15] Node: '.'|        |  |  +-[108] Node: 'com'|        |  |  \-[108] Node: 'tydhot'|        |  \-[108] Node: 'eninty'
|        \-[108] Node: 'User'

在完成了optionalFromTokenClause规则后,将得到的树的根节点作为update的子节点,也是update的第一个子节点,被down所指向,而update的其他子节点将会被from节点所指向。

optionalFromTokenFromClause();
astFactory.addASTChild(currentAST, returnAST);
setClause();
astFactory.addASTChild(currentAST, returnAST);

回到update规则,接下来会强制进入setClause部分的语法匹配。接下来经过语法规则编译的代码部分与上文类似,重点解析语法规则。

setClause: (SET^ assignment (COMMA! assignment)*);
assignment: stateField EQ^ newValue;
stateField: path;
newValue: concatenation;

在setClause的语法规则中,根据正则,则会强制从set关键字进行匹配,接下来也强制至少存在一个等号表达式,复数的表达式可以通过逗号之间隔开,而在等号左边,则支持path规则,也就是上文中通过.隔开常量的java对象形式的表达,而右边则是一个concatenation规则,也就是被赋值的值。

concatenation: additiveExpression ( c:CONCAT^ { #c.setType(EXPR_LIST); #c.setText("concatList"); } additiveExpression( CONCAT! additiveExpression )* { #concatenation = #([METHOD_CALL, "||"], #([IDENT, "concat"]), #c ); } )?
;

赋值语句分为五级,第4级也就是优先度最低的通过|隔开.

additiveExpression: multiplyExpression ( ( PLUS^ | MINUS^ ) multiplyExpression )*
;

第3级则是根据加减分隔,根据先序遍历的性质,这里也将会在接下来的乘除操作之后进行加减。

multiplyExpression: unaryExpression ( ( STAR^ | DIV^ | MOD^ ) unaryExpression )*
;

第2级也就是乘除,优先级高于加减。

unaryExpression: MINUS^ {#MINUS.setType(UNARY_MINUS);} unaryExpression| PLUS^ {#PLUS.setType(UNARY_PLUS);} unaryExpression| caseExpression| quantifiedExpression| atom;caseExpression// NOTE : the unaryExpression rule contains the subQuery rule: simpleCaseStatement| searchedCaseStatement;simpleCaseStatement: CASE^ unaryExpression (simpleCaseWhenClause)+ (elseClause)? END! {#simpleCaseStatement.setType(CASE2);};simpleCaseWhenClause: (WHEN^ unaryExpression THEN! unaryExpression);elseClause: (ELSE^ unaryExpression);searchedCaseStatement: CASE^ (searchedCaseWhenClause)+ (elseClause)? END!;searchedCaseWhenClause: (WHEN^ logicalExpression THEN! unaryExpression);quantifiedExpression: ( SOME^ | EXISTS^ | ALL^ | ANY^ ) ( identifier | collectionExpr | (OPEN! ( subQuery ) CLOSE!) )
;

第一级则是一些逻辑操作,优先度是除了常量和数字之外最高的。

atom: primaryExpression(DOT^ identifier( options { greedy=true; } :( op:OPEN^ {#op.setType(METHOD_CALL);} exprList CLOSE! ) )?| lb:OPEN_BRACKET^ {#lb.setType(INDEX_OP);} expression CLOSE_BRACKET!)*;
// level 0 - the basic element of an expression
primaryExpression: { validateSoftKeyword("function") && LA(2) == OPEN && LA(3) == QUOTED_STRING }? jpaFunctionSyntax| { validateSoftKeyword("cast") && LA(2) == OPEN }? castFunction| identPrimary ( options {greedy=true;} : DOT^ "class" )?| constant| parameter| OPEN! (expressionOrVector | subQuery) CLOSE!;

第0级也就是一些常量和数字,是所有词中,被处理优先级最高的,这些也将在serClause规则中依次被处理成为语法树中的节点。

我们的例子在经过set规则后,生成的树的根节点将会被之前的from部分的right所指向,代表同为update的节点的子节点,在语句的顺序上,也在其兄弟节点from节点的之后,生成的语法树会成为如下所示。

 \-[50] Node: 'update'+-[22] Node: 'FROM'|  \-[90] Node: 'RANGE'|     \-[15] Node: '.'|        +-[15] Node: '.'|        |  +-[15] Node: '.'|        |  |  +-[108] Node: 'com'|        |  |  \-[108] Node: 'tydhot'|        |  \-[108] Node: 'eninty'|        \-[108] Node: 'User'+-[46] Node: 'set'|  \-[105] Node: '='|     +-[108] Node: 'userName'|     \-[127] Node: ':'
|        \-[108] Node: 'userName'

最后根据我们一开始的语法规则,where关键字的匹配是可有可无的,但是我们的例子给出了where 关键字,所以同样的也要相应的继续whereClause规则的匹配。

whereClause: WHERE^ logicalExpression;
ogicalExpression: expression;
// Main expression rule
expression: logicalOrExpression;
// level 7 - OR
logicalOrExpression: logicalAndExpression ( OR^ logicalAndExpression )*;
// level 6 - AND, NOT
logicalAndExpression: negatedExpression ( AND^ negatedExpression )*;
// NOT nodes aren't generated.  Instead, the operator in the sub-tree will be
// negated, if possible.   Expressions without a NOT parent are passed through.
negatedExpression!
{ weakKeywords(); } // Weak keywords can appear in an expression, so look ahead.: NOT^ x:negatedExpression { #negatedExpression = negateNode(#x); }| y:equalityExpression { #negatedExpression = #y; }
;
equalityExpression: x:relationalExpression (( EQ^| is:IS^   { #is.setType(EQ); } (NOT! { #is.setType(NE); } )?| NE^| ne:SQL_NE^ { #ne.setType(NE); }) y:relationalExpression)* {// Post process the equality expression to clean up 'is null', etc.#equalityExpression = processEqualityExpression(#equalityExpression);};

Where的语法规则,在之前set的5级基础上又添加了三级,分别是第七级的与,第六级的是非,和第五级的逻辑等式集合规则。

经过上述的语法规则后,生成的语法树加入到最后的结果如下。

\-[50] Node: 'update'+-[22] Node: 'FROM'|  \-[90] Node: 'RANGE'|     \-[15] Node: '.'|        +-[15] Node: '.'|        |  +-[15] Node: '.'|        |  |  +-[108] Node: 'com'|        |  |  \-[108] Node: 'tydhot'|        |  \-[108] Node: 'eninty'|        \-[108] Node: 'User'+-[46] Node: 'set'|  \-[105] Node: '='|     +-[108] Node: 'userName'|     \-[127] Node: ':'|        \-[108] Node: 'userName'\-[52] Node: 'where'\-[105] Node: '='+-[108] Node: 'userId'\-[127] Node: ':'\-[108] Node: 'userId'

在语法树中,关键字部分下的子树通过中序遍历,都可以得到例子中的子句,最后将得到的字句根据关键字的顺序组合,就能得到例子中的hql语句,这个语法树,也将被用来作为转换sql语句的依据。

hibernate中antlr对于hql生成抽象语法树源码解析相关推荐

  1. hibernate中antlr对于hql与sql的转换源码的一些细节

    Hibernate 5.1.11Final 关于hql中的对象类转换成表名. 在from模块里对hql抽象语法树进行匹配的时候,在path()规则会还原在对hql进行语法解析的时候生成的语法树. pu ...

  2. hibernate中antlr对于hql的词法分析源码解析

    Hibernate版本 5.1.11 private HqlParser parse(boolean filter) throws TokenStreamException, RecognitionE ...

  3. Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析

    Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析 生鲜电商搜索引擎的特点 众所周知,标准的搜索引擎主要分成三个大的部分,第一步是爬虫系统,第二步是数据分析,第三步才 ...

  4. java 中线程池的种类,原理以及源码解析(1)

    java 中的线程池创建都是Executors 类中提供的方法,并且方法返回线程池对象. Executors 源码: // // Source code recreated from a .class ...

  5. ReentrantLock中lock/trylock/lockInterruptibly方法的区别及源码解析

    看了几篇关于这三者区别的文章,但都说的不够具体,自己去读了下源码,大概是清楚了三者的功能,不想了解源码的可以跳到最后看总结. 首先,ReentrantLock类中使用了大量的CAS操作,也就是Comp ...

  6. Java解析SQL生成语法树_04. Hive源码 — HQL解析(抽象语法树的生成和语义分析)

    HQL的解析过程主要在Driver中的compile方法,这一些主要看这个方法中的代码. 1. compile中的主要内容 public int compile(String command, boo ...

  7. 抽象语法树在 JavaScript 中的应用

    抽象语法树是什么 在计算机科学中,抽象语法树(abstract syntax tree 或者缩写为 AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语 ...

  8. AST抽象语法树的基本思想

    AST抽象语法树的基本思想 前言 AST概述 AST结构 AST解析 转换 生成 前言 在阅读java ORM框架spring data jpa的源码时,发现Hibernate(spring data ...

  9. php ast 抽象语法树,AST抽象语法树的基本思想

    AST抽象语法树的基本思想 前言 AST概述 AST结构 AST解析 转换 生成 前言 在阅读java ORM框架spring data jpa的源码时,发现Hibernate(spring data ...

最新文章

  1. linux 后台运行jar SpringBoot
  2. Android 之 Fagment 完全解析
  3. K8s-V1.17.6支持GPU
  4. php获取网络文件的几种方式,PHP如何实现获取网络上的文件?
  5. 大工19春计算机文化基础在线测试1,大工19春《计算机文化基础》在线测试1(含答案)...
  6. 上传文件时显示选择窗口
  7. mysql的order by,group by和distinct优化
  8. 自动选择SVG和VML的WEB页面
  9. 某些数组和字符串类型转换(转)
  10. java 利用Future异步获取多线程任务结果
  11. 网络安全法学习整理笔记
  12. 如何利用Python进行数据分析
  13. GC bias GC偏好
  14. 一文读懂Elephant Swap的LaaS方案的优势之处
  15. 互联网日报 | 4月8日 星期四 | 蔚来第10万辆量产车下线;哈啰进入电单车生产销售领域;携程宣布正式开始港股招股...
  16. iOS 根据银行卡号判断银行名称
  17. 电脑截屏怎么固定到屏幕 截图放在桌面上
  18. RAM、 ROM 、SRAM 、DRAM 、SDRAM 、DDR (2、3、4)SDRAM辨析
  19. 深度学习图像分类:Kaggle植物幼苗分类(Plant Seedlings Classification)完整代码
  20. Android源码编译流程及所需的编译环境

热门文章

  1. aurora 初学页面元素
  2. Notepad++使用技巧
  3. element-ui表单校验
  4. basler相机的触发线是那两脚_探究机器视觉领域线扫相机和面阵相机的区别
  5. 获取字段_数据库中敏感字段的标记、标示
  6. 关于LabVIEW视觉ROI的读取与存储
  7. 【坐在马桶上看算法】啊哈算法13:零基础彻底弄懂“并查集“
  8. .net core 后台 post设置等待时间_[vueelementadmin]前端发送的post请求的数据,后端接收不到并报EOFException异常的解决方案...
  9. 前端每日实战 2018 年 9 月份项目汇总(共 26 个项目)
  10. window.btoa()方法;使字符编码成base64的形式