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