• 一个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数据库,其他数据库管理系统不详。

    
  • 相关阅读:
    解析Javascript事件冒泡机制
    LeetCode——Flatten Binary Tree to Linked List
    流动python
    HDU2586
    Cannot find ActionMappings or ActionFormBeans collection
    reactor设计模式
    简单的Ajax应用实例
    CString——Left、Right、Find、ReverseFind
    MATLAB新手教程
    八大排序算法总结
  • 原文地址:https://www.cnblogs.com/yxysuanfa/p/7110301.html
Copyright © 2020-2023  润新知