背景:
hibernate已经是众所周知的全自动ORM框架,使用起来非常便捷,但是有的时候自己也需要写sql进行查询(为什么不用mybatis?我进公司的时候框架里边就用了hibernate,由于业务不断扩大,想改造成mybatis代价很大);在使用hibernate进行sql查询时候,出现Column ‘xxx’ not fund异常;
问题分析:
1、sql有问题,查询的字段数据库没有?
将执行的sql贴到mysql客户端执行无异常,证明不是sql的问题
2、hibernate3自带bug?
进行hibernate源码查阅,整个程序执行步骤如下:
第一步:通过EntityManager的createNativeQuery(sql)方法获取Query对象
第二步:将获取到的Query设置查询参数,然后调用query.getResultList()方法,此方法真正进行查询处理,内部实现如下:
a、通过第一步源码得知返回的是实现Query接口的QueryImpl对象,所以直接查看QueryImpl里面的getResultList()方法:
query对象是第一步的时候QueryImpl实例化传入进来的,得知query其实就是SqlQuery:
SqlQuery是个接口,由图可知,SqlQuery的实现就只有SqlQueryImpl:
b、进入到sqlQueryImpl的list()方法,得知调用的是getSession().list(spec, getQueryParameters(namedParams))方法
getSession()返回的是SessionImpl对象,进入SessionImpl的list(NativeSQLQuerySpecification spec, QueryParameters queryParameters)方法:
进入listCustomQuery方法:
CustomLoader是真正处理查询的类,调用关系如图:
buildResultRow方法才是正在将resultSet一行数据转成结果,源码:
由源码可知,最后通过columnProcessors来处理,columnProcessors是CustomLoader.ResultColumnProcessor数组
ResultColumnProcessor的实现有两个,通过调试本次查询使用的是ScalarResultColumnProcessor类
最终核心执行方法:
performDiscovery:这个方法是获取查询字段的列名
extract:这个方法核心调用是根据列名从resultSet里面获取值,并根据数据中的字段类型自动转型
到这里问题出现的原因差不多就知道了:
由于是根据列名从resultSet获取值,但是sql里面存在别名的字段情况下,是获取不到改字段的;比如:
使用SELECT name as uname FROM user进行查询,列名是:name,别名是uname,如果使用resultSet.getObject(“name”)会报找不到列的错误,resultSet.getObject(“uname”)是可以正确获取到值
解决办法:
重新实现CustomLoader,继承CustomLoader并重写getResultColumnOrRow方法;
注意:由于CustomLoader是直接在SessionImpl的listCustomQuery方法里面直接实例化的,并没有提供改类的set方法由调用者传入,所以必须实现自定义SessionImpl实现
如有问题,请多多指教,谢谢