一个NHibernate的BUG
一、背景
我们如今做的项目,用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出现的情景例如以下:
- 使用原生SQL訪问数据库;
- 设置了FirstResult、MaxResults等查询结果范围;
- 最早的引入版本号不详,最新的版本号中。从源码上推断。此问题仍然存在。
- 我们使用的是Oracle数据库,其他数据库管理系统不详。
一个NHibernate的BUG的更多相关文章
- [NHibernate]第一个NHibernate的应用配置
NHibernate是.Net平台下一个成熟的,开源的对象关系映射器(ORM).本文来介绍第一次使用NHibernate的时候的配置. 1.下载NHibernate.Nhibernate官网最新版本为 ...
- [转]NHibernate之旅(2):第一个NHibernate程序
本节内容 开始使用NHibernate 1.获取NHibernate 2.建立数据库表 3.创建C#类库项目 4.设计Domain 4-1.设计持久化类 4-2.编写映射文件 5.数据访问层 5-1. ...
- Nhibernate学习教程(2)-- 第一个NHibernate程序
NHibernate之旅(2):第一个NHibernate程序 本节内容 开始使用NHibernate 1.获取NHibernate 2.建立数据库表 3.创建C#类库项目 4.设计Domain 4- ...
- NHibernate从入门到精通系列(3)——第一个NHibernate应用程序
内容摘要 准备工作 开发流程 程序开发 一.准备工作 1.1开发环境 开发工具:VS2008以上,我使用的是VS2010 数据库:任意关系型数据库,我使用的是SQL Server 2005 Expre ...
- 一个iOS6系统bug+一个iOS7系统bug
先看实际工作中遇到的两个bug:(1)iPhone Qzone有一个导航栏背景随着页面滑动而渐变的体验,当页面滑动到一定距离时,会改变导航栏上title文本的颜色,但是有一个莫名其妙的bug,如下:
- FIREDAC(DELPHI10 or 10.1)提交数据给ORACLE数据库的一个不是BUG的BUG
发现FIREDAC(DELPHI10 or 10.1)提交数据给ORACLE数据库的一个不是BUG的BUG,提交的表名大小写是敏感的. 只要有一个表名字母的大小写不匹配,ORACLE就会认为是一个不认 ...
- pycharm下: conda installation is not found ----一个公开的bug的解决方案
pycharm conda installation is not found ----一个公开的bug的解决方案 pycharm+anaconda 是当前的主流的搭建方案,但是常出现上述问题. ...
- 一个神奇的bug:OOM?优雅终止线程?系统内存占用较高?
摘要:该项目是DAYU平台的数据开发(DLF),数据开发中一个重要的功能就是ETL(数据清洗).ETL由源端到目的端,中间的业务逻辑一般由用户自己编写的SQL模板实现,velocity是其中涉及的一种 ...
- salesforce零基础学习(一百一十五)记一个有趣的bug
本篇参考:https://help.salesforce.com/s/articleView?language=en_US&type=1&id=000319486 page layou ...
随机推荐
- MFC学习篇(二):error LNK2005 及其解决方法
环境:MFC条件下添加原有代码 >nafxcwd.lib(afxmem.obj) : error LNK2005: @YAPAXI@Z) already defined in LIBCMTD.l ...
- BZOJ 3998 后缀数组
思路: 第一问 建出来后缀数组以后 前缀和一发n-sa[i]-ht[i]+1 二分 第二问 二分判断是带重复的第几 怎么判断呢 找到它 往后扫ht递减sum+=它 跟K判判 注意等于 加 ...
- BZOJ 4668 LCT
思路: 这不是LCT裸题嘛23333 (好像并查集+按秩合并就可以搞了 我还是too young) 维护边权的话 就新加一个点 代表边 这个点想线段的两个端点连边就好了 //By SiriusRen ...
- ACM_水题你要信了(修改版)
水题你要信了 Time Limit: 2000/1000ms (Java/Others) Problem Description: 某发最近又认识了很多妹(han)子,可是妹(han)子一多不免有时会 ...
- jQuery里$.post请求,后台返回结果为“json”格式,前台解析错误问题记录
在JSP页面使用$.post请求后台返回json数据时,在最后 必须加上返回数据格式为json的才行.不然JSP页面解析会出错.
- (二)Python 学习第二天--爬5068动漫图库小案例
(注:代码和网站仅仅是学习用途,非营利行为,源代码参考网上大神代码,仅仅用来学习
- 【Centos7】Tomcat安装及一个服务器配置多个Tomcat
完成解压 参考 http://www.cnblogs.com/h--d/p/5074800.html https://www.cnblogs.com/tudou-22/p/9330875.html 步 ...
- 取三级分销上下级用户id
//取上三级的用户idpublic function _get_up_third_id($member_id){ $up_id=array(); $invite_id=dbselect('invite ...
- react typescript 子组件调用父组件
//父组件 import * as React from 'react'import { Input } from 'antd'const Search = Input.Searchimport &q ...
- for循环中索引值和取值的迷惑
利用for循环和range从100——10,倒序讲所有的偶数添加到一个新列表中,然后对列表的元素进行筛选,将能被4整除的数留下来. even = []for i in range(100,9,-1): ...