最近在改一份二手代码的时候,项目运行报了个java.lang.IllegalArgumentException: node to traverse cannot be null异常。
WTF?!难道我HQL写错了?!我只是添加了一个update方法而已啊!java
这里使用的是JPA的Query注解,其实语法跟HQL是同样的,我已经把这行HQL每一个空格都TM检查过了,没有发现任何奇怪的东西,没办法了只好调试一下源码node
最早抛出异常的是在Hibernate的orghibernatehqlinternalastutilNodeTraverser.java:46,这里判断若是AST为空,则抛出异常,那AST究竟是个啥啊?
经过跟踪NodeTraverser的调用,可看到ACT是从parser获取的,而这里的parser实际上就是Hibernate的Hql语法分析器!所以网上不少文章都会得出本文提到的异常就是HQL语法错误致使的了。可是我这个HQL明显没有语法错误的,问题又出在哪呢?咱们加个短点瞧一瞧:spa
好玩的事情来了,若是HQL是select开头的话,是不会报错的hibernate
等到一条update了,果真parser处理后的hqlAst是空的对比上面Select语句就能够明显看出问题所在了:问题出在了parser.statement()里,那跟进去看看囖:
逐行调试,发如今执行updateStatement()时抛出异常,再跟进去:
跑到default去了调试
由于LA(1)是41,不在switch的任何分支里,而后实际上我在这花了不少时间,都浪费在看antrl的源码上了,就是想搞明白LA是在那里设值的,结果越看越懵逼,但实际上咱们能够换个思路,经过监控每一步执行后的各个变量能够发现有这样的规律:ip
上图是在执行match(UPDATE)前,各个主要变量如图所示源码
直到执行了match方法后,LA(1)变为了41,而同时,LT(1)里的值引发了个人注意:
上面已经提到过了,实际上这部分代码是Hibernate的HQL语法解析器,那讲道理的话,第一次执行,处理完UPDATE关键字,日后应该是第二个关键字,而实际上咱们的HQL中根被没有order这个词啊,为何会致使报错呢?
还记得上面的某个断点么,就是调用parser.statement()的地方,来看看交由parser处理时的hql是什么样子的it
大家发现问题了吗?Hibernate在处理hql的时候,是会把包名补全的,而这个实体类的包名是以order开头的!update关键字后不能有order关键字...io