一、背景

我们如今做的项目,用NHibernate实现数据訪问层。

訪问数据时,有的数据库表是确定的:有明白的表名、字段名。这时候依照常规的方法处理就可以:建立数据库表到类的映射。使用HQL读写数据库。

但有的数据訪问,所针对的数据库表是不确定的,在执行阶段确定訪问哪些数据库表的哪些字段。

数据库表和字段都不确定,自然没办法建议O-R映射,仅仅好构造SQL语句了。

既然已经用了NHibernate,我们利用SQL訪问数据库时,也仍然使用NHibernate。主要是想利用它管理的Session,还有对分页查询的支持:能够指定開始行。还有所须要的总行数。

就由于贪这个廉价。出问题了。

二、错误

使用SQL(而不是HQL)訪问数据库。并且加上訪问范围(開始行或总行数),有时候会出现奇怪的问题:有的字段,在数据库中明明有值,但就是读不出来。

比如。有一个数据库表,表的定义大致是:MyTable(Id, Name, FromPoint)。

如今须要查询当中的前10条记录,利用以下的SQL语句:

select Id, Name, FromPoint from MyTable

创建好SQL查询后,设置查询范围:

...
var query = session.CreateSQLQuery(sql);
query.SetFirstResult(0);
query.SetMaxResults(10);
...

对于运行结果,期望的是,每条记录有三个字段。但实际上,仅仅返回前两个字段的值,第三个字段,FromPoint的值。没有返回。

查看NHibernate的日志,所生成的SQL是:

select Id, Name from (select Id, Name, FromPoint from MyTable) where rownum<=10

这实在令人费解。

三、原因

在网上搜索。找不到原因。

仅仅好祭出最后的杀手锏:跟踪源码。

结论是:NHibernate在解析SQL语句时有问题。导致过滤掉了不该过滤掉的列。

详细地说,在类 NHibernate.Dialect.Dialect的方法 ExtractColumnOrAliasNames 中有例如以下代码:

if (token.StartsWithCaseInsensitive("select"))
continue;
if (token.StartsWithCaseInsensitive("distinct"))
continue;
if (token.StartsWithCaseInsensitive(","))
continue;
if (token.StartsWithCaseInsensitive("from"))
break;

这段代码的本意。是不将select、distinct等SQL保留字作为列。并且一旦遇到from保留字。就意味着列结束。

问题在于,它用了StartsWith,而不是整词推断。从而误伤了以这些keyword开头的字段。并且一旦有以from開始的字段,后面的字段都被过滤掉了。

四、办法

出问题的方法 ExtractColumnOrAliasNames 是 static internal 的。没有改动机会。

我们仅仅好绕道走:自行加上rownum的过滤条件。

本来要借助NHibernate实现跨数据库,我还一直告诫小伙伴们避免使用Oracle、SQL Server等数据库管理系统特定的SQL语法来着。

不少程序猿会本能地想到一个解决方法:既然是开源的,并且错误原因找到了,拿过来改动好。编译一个新版本号用就OK。我对此明白表示不赞成,一方面。这会脱离NHibernate主线版本号的发展。还有一方面,也给组件的引用带来不便:我们正在用Spring.NET管理NHibernate。build一个自己的版本号。要设置一堆元信息。想想就头大。

五、范围

就我眼下所知,该BUG出现的情景例如以下:

  1. 使用原生SQL訪问数据库;
  2. 设置了FirstResult、MaxResults等查询结果范围;
  3. 最早的引入版本号不详,最新的版本号中。从源码上推断。此问题仍然存在。
  4. 我们使用的是Oracle数据库,其他数据库管理系统不详。



一个NHibernate的BUG的更多相关文章

  1. [NHibernate]第一个NHibernate的应用配置

    NHibernate是.Net平台下一个成熟的,开源的对象关系映射器(ORM).本文来介绍第一次使用NHibernate的时候的配置. 1.下载NHibernate.Nhibernate官网最新版本为 ...

  2. [转]NHibernate之旅(2):第一个NHibernate程序

    本节内容 开始使用NHibernate 1.获取NHibernate 2.建立数据库表 3.创建C#类库项目 4.设计Domain 4-1.设计持久化类 4-2.编写映射文件 5.数据访问层 5-1. ...

  3. Nhibernate学习教程(2)-- 第一个NHibernate程序

    NHibernate之旅(2):第一个NHibernate程序 本节内容 开始使用NHibernate 1.获取NHibernate 2.建立数据库表 3.创建C#类库项目 4.设计Domain 4- ...

  4. NHibernate从入门到精通系列(3)——第一个NHibernate应用程序

    内容摘要 准备工作 开发流程 程序开发 一.准备工作 1.1开发环境 开发工具:VS2008以上,我使用的是VS2010 数据库:任意关系型数据库,我使用的是SQL Server 2005 Expre ...

  5. 一个iOS6系统bug+一个iOS7系统bug

    先看实际工作中遇到的两个bug:(1)iPhone Qzone有一个导航栏背景随着页面滑动而渐变的体验,当页面滑动到一定距离时,会改变导航栏上title文本的颜色,但是有一个莫名其妙的bug,如下:

  6. FIREDAC(DELPHI10 or 10.1)提交数据给ORACLE数据库的一个不是BUG的BUG

    发现FIREDAC(DELPHI10 or 10.1)提交数据给ORACLE数据库的一个不是BUG的BUG,提交的表名大小写是敏感的. 只要有一个表名字母的大小写不匹配,ORACLE就会认为是一个不认 ...

  7. pycharm下: conda installation is not found ----一个公开的bug的解决方案

    pycharm  conda installation is not  found ----一个公开的bug的解决方案 pycharm+anaconda 是当前的主流的搭建方案,但是常出现上述问题. ...

  8. 一个神奇的bug:OOM?优雅终止线程?系统内存占用较高?

    摘要:该项目是DAYU平台的数据开发(DLF),数据开发中一个重要的功能就是ETL(数据清洗).ETL由源端到目的端,中间的业务逻辑一般由用户自己编写的SQL模板实现,velocity是其中涉及的一种 ...

  9. salesforce零基础学习(一百一十五)记一个有趣的bug

    本篇参考:https://help.salesforce.com/s/articleView?language=en_US&type=1&id=000319486 page layou ...

随机推荐

  1. python django简单操作

    准备: pip3 install  django==1.10.3 cmd django-admin startproject  guest  创建一个guest的项目 cd guest manage. ...

  2. SQL Server语言 函数以及SQL编程

    1.数学函数:操作一个数据,返回一个结果 --去上限: ceiling ☆select ceiling(price) from car --去下限:floor ☆select floor(price) ...

  3. .net framework 3.5 安装报错 0x800F0954问题

    windows Server 2019 .net framework 3.5 安装报错 0x800F0954问题 .net framework 3.5的安装教程:但是安装出现0x800F0954这个错 ...

  4. SAS进阶《深入解析SAS》之SAS数据挖掘的一般流程

    SAS进阶<深入解析SAS>之SAS数据挖掘的一般流程 1. 所谓数据挖掘,是指通过对大量的数据进行选择.探索与建模,来揭示包含在数据中以前不为人所知的模式或规律,从而为商业活动或科学研究 ...

  5. 数字化婚姻配对尝试问题(C++实现)

    问题描述:一.标题: 数字化婚姻配对尝试 二.题目: 建立一个模型,来模拟推导社会男女择偶过程. 为了模型简化,一个人的特性指标有三个,这里假设为财富.样貌.品格,每个指标均可取值1-100之间任意数 ...

  6. git 缓存密码导致的不能和远程仓库交互unable to access... 403错误

    尝试了各种方式,包括卸载等最终解决方案: 查看本机的credential 是否已经被清空. 如果输入了 git config credential.helper 命令之后没有输出,说明 git 的配置 ...

  7. [工具]iostat

    本文主要分析了Linux的iostat命令的源码 iostat源码共563行,应该算是Linux系统命令代码比较少的了.源代码中主要涉及到如下几个Linux的内核文件: 1./proc/disksta ...

  8. TCP/IP UDP 协议首部及数据进入协议栈封装的过程

    数据的封装 UDP 封装 TCP 封装 IP 封装 检验和算法 当应用程序用TCP传送数据时,数据被传送入协议栈中,然后逐一通过每一层直到被当作一串比特流送入网络 注: UDP数据TCP数据基本一致. ...

  9. python爬虫18 | 就算你被封了也能继续爬,使用IP代理池伪装你的IP地址,让IP飘一会

    我们上次说了伪装头部 ↓ python爬虫17 | 听说你又被封 ip 了,你要学会伪装好自己,这次说说伪装你的头部 让自己的 python 爬虫假装是浏览器 小帅b主要是想让你知道 在爬取网站的时候 ...

  10. 第十二节:pandas缺失数据处理

    1.isnull():检查是否含有确实数据 2.fillna():填充缺失数据 3.dropna() :删除缺失值 4.replace():替换值