BD到Objects的转换时使用Map的一个陷阱.
数据从BD到Objects的转变:
上一篇中, 描述了下问题的引起及其解决, 这篇中将结合Hibernate的query机制来看看问题发生的根源.
先看hibernate执行query时的工作原理, 它将分三步走: 第一步以Mapping配置为出发点根据用户的调用生成sql,第二步把生成的sql发往数据库等待结果返回, 第三步从数据库返回的结果集里取出数据转用Class来封装.在上篇中, 我们通过执行hibernate生成的sql看到resultset没问题, 这样先断定问题出在了hibernate对返回结果的解析/封装上. 那么hibernate在解析/封装DB中返回的结果时又都有哪些策略呢? 这就涉及到ResultTransformer这个接口, hibernate是通过这个接口里方法把DB返回的结果集用具体的Model对象(或纯粹的java.lang.Object对象)来封装的. 再看hibernate的源码,我们发现这个接口在hibernate中有如下七种默认实现:
AliasToBeanConstructorResultTransformer
AliasToEntitymapResultTransformer
DistinctRootEntityResultTransformer
PassThroughResultTransformer
RootEntityResultTransformer
ToListResultTransformer
这 七种实现我们不一一做介绍, 这里只说下AliasToEntitymapResultTransformer这个实现,它也就是上一篇程序中用的transformer.顾名思义, 这个实现是将DB中返回的结果集转为Map, 这个map是以添加Projections时的别名为key以期望值为value的,我们得到这些map后(这些map又一下子放到一个list里),再 手动地将结果集转为其它实用的Java类(现在想来这些手动的转换其实是可以自动完成的, 我想在下一篇中记录这方面的试验心得).
上面这个过程就是往Map里放值, 再在自己的程序中通过key来取值了, 问题也就出在这里.
projList.add(Projections.sum("contractBw"),"contractBw"); -- (1)
我们看到上面这句是对contractBw求和, 并把求和后的结果以contractBw这样别名保存了下来,放到结果map中, 而这句:
projList.add(Projections.groupProperty(groupItem),groupItem); -- (2)
是负责设定分组的,其中的groupItem是从外面外来的, 它有三种情况, 其中一个就是contractBw, 这样就麻烦了, 不管(1)句里别名先放还是(2)句里的别名先放,都会有一个被覆盖掉.map嘛, 它有自己的规则.
于是后面取值时,不管是map.get(" contractBw ")还是map.get(groupItem), 所得的结果是一样的.
至此问题分析完了, 那么这段经历对自己以后的写程序有什么启示呢?
1,写类似上面这样的公共代码时,一定考虑到client传来参数的所有可能性.若刚开始写程序时留点意的话,也不至于有后面的debug过程.
2,遇到问题时, 多看源码,并在源码上设置断点, 有些问题在网上是查不到的, 或者说不好查到的, 只有看源码时的解决才能真正集中火力围攻问题.
3,英语学习不能丢, 这次问题中, 若不是自己保持了较好的英语语感,也不可能在短时间内捕捉到问题的根源. 毕竟有太多的开源框架是以英语为母语的牛人写的, 看里面类和方法时, 有了较好的英语语感,理解上会顺利的多,像这里map,transformer这样很常用的单词就给了我很大的帮助.
4,基础知识的重要性,这次问题的解决,若不是自己的Java基础知识还算扎实的话, 解决也不可能那么顺利.