一、背景

我们如今做的项目,用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. HDU 1847 博弈

    sg[0]=0; sg[i]=mex{sg[i-2^(j)]}  (i>=2^j) mex()为不在此集合的最小非负整数 #include <stdio.h> #include &l ...

  2. C#入门经典 Chapter4 流程控制

    4.1布尔逻辑 布尔比较运算符 ==  !=   <   >    <=    >= 处理布尔值的布尔值运算符 ! & | ^(异或) 条件布尔运算符 &&am ...

  3. JS——高级各行换色

    1.获取tbody下的子元素 2.注册鼠标覆盖事件时存储当时的背景颜色,注册鼠标离开事件时把存储的颜色赋值注册事件对象 <!DOCTYPE html> <html> <h ...

  4. SQL基本操作——GROUP BY

    GROUP BY 语句用于结合合计函数,根据一个或多个列对结果集进行分组. SQL GROUP BY 实例:我们拥有下面这个 "Orders" 表 O_Id OrderDate O ...

  5. CSS居中布局方案

    基本结构 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...

  6. python 将中文转拼音后填充到url做参数并写入excel

    闲着没事写了个小工具,将中文转拼音后填充到url做参数并写如excel 一.先看下演示,是个什么东西 二.代码 代码用到一个中文转拼音的库,库是网上下的,稍微做了下修改,已经找不原来下载的地址了,然后 ...

  7. js 判断 微信浏览器

    <script type="text/javascript"> window.onload = function() { isWeixinBrowser(); } // ...

  8. day09-文件的操作

    目录 文件的基本操作 文件 什么是文件 如何使用文件 打开&关闭文件 打开&关闭文件 del f和f.close()的区别 文件路径 打开模式(不写默认是r) 编码格式 补充(open ...

  9. listcontrol 加combobox

    之前写过一篇(list Control实现单元格编辑)文章,那篇文章不是很完善执行的时候有时会出错,这篇文章经过完善后还加入了Combo Box功能! 这里我就只是晒一下我的代码; 头文件: // L ...

  10. BZOJ 3489: A simple rmq problem KDtree

    Code: #include<bits/stdc++.h> #define maxn 200000 #define inf 100000000 #define mid ((l+r)> ...