zoukankan      html  css  js  c++  java
  • 一个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出现的情景例如以下:

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

    
  • 相关阅读:
    软件工程之旅开始啦
    c# async,await, 委托函数
    mysql 访问不是本地数据库,给用户刷新了权限没有作用
    c# WndProc事件 消息类型
    sql not in 优化问题
    c# dataGridView 表头格式设置不管用
    sql 更新多条记录
    mysql 插多行数据
    win7 64bit+vs2010 操作注册表
    bat脚本命令
  • 原文地址:https://www.cnblogs.com/yxysuanfa/p/7110301.html
Copyright © 2011-2022 走看看