一个NHibernate的BUG

摘要:
现在我们已经使用了NHibernate,当我们使用SQL访问数据库时,我们仍然使用NHibernate。查看NHibernate的日志,生成的SQL是:selectId,Namefromwhererownum˂=10,这真是令人费解。我打算使用NHibernate实现跨数据库,我一直警告我的朋友避免使用Oracle、SQL Server和其他数据库管理系统的特定SQL语法。这将脱离NHibernate主线版本号的开发。另一方面,这也给组件的引用带来了不便:我们正在使用Spring。NET来管理NHibernate。
一、背景

我们如今做的项目,用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》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Weex 标签控件pm2 start命令进阶详解下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

oracle 11g 安装与卸载

安装 点击是,这是位数不一致,但可用。 桌面类――这种安装方式一般适用于台式机和笔记本。它包含一个最小数据库和最低的配置需求。 服务器类――这种安装方式适用于服务器,例如,它会向您提供数据中心和用于支持企业级的应用程序。如果您需要使用高级配置选项,请选择此安装类型。 在桌面类中,只有基本选择项。 在服务器类,可以选择标准安装(仅让您作有基本选择)或...

ThinkPHP框架使用笔记

SQL日志问题 THINK_VERSION : 5.0.13 SQL访问日志,默认在debug模式下才会打印出来。 测试发现: 浏览器访问、数据库调试模式 debug为false日志不会打印SQL日志,为true会打印日志 命令行访问、数据库调试模式debug对打印SQL日志没有影响都会打印出来。可以设置全局debug模式为false,则不会打印sql日志...

Jmeter之JDBC请求(四)

我们常用的Jmeter中的功能又HTTP请求、JDBC Request、SOAP/XML -RPC Request,这3个请求, 现在就为大家介绍下 什么是JDBC请求 首先,大家右键点击“测试计划”与右键“线程组”可以比较看的出,线程组中多了一个Sampler 这边既然要介绍JDBC请求,那么就必须添加一个驱动,使数据库跟Jmeter能连接上 咋们这...

mysql安装使用

  linux系统 mysql-5.7.14-linux.zip部署包支持在CentOS 6.x/7.x 服务器硬盘大小要求     a) /data/mysql_data  如果存在该独立分区,要求该分区 >10G b) 如果仅存在 /data 分区, 要求该分区 >10G c) 否则,要求根分区/ > 10G MySQL_INST...

django项目中.gitignore文件忽略上传的文件,以及数据库迁移文件到底是否需要上传吗?

Django项目上传。gitignore文件建议忽略文件清单以及是否转移数据库的迁移文件: Django项目开发或改动将本地代码提交到代码库时,我们需要忽略部分文件上传,占用内存; 1.创建.gitignore文件: 终端进入项目文件夹内,而不是app文件夹 touch .gitignore vim .gitignore 在.gitignore中写入你要忽...

Bartender 使用 Excel xlsx 数据库时出现 0x800A0E7A

Bartender 使用 Excel 数据库时出现 0x800A0E7A 这是因为没有装 数据库驱动的原因。 安装微软的驱动就可以。 注意是安装 32 位的驱动。 https://www.microsoft.com/zh-TW/download/details.aspx?id=13255...