1

目前,在使用 Oracle 10g 和 NHibernate 分页数据时,我在我们的应用程序中遇到了一些奇怪的行为。

我有一个有 20 行的表,我想在列表中显示每个站点 10 行,所以我的列表有 2 页。

使用 NHibernate 我正在为第一页query.SetMaxResults(10).SetFirstResult(0)和第二页设置query.SetMaxResults(10).SetFirstResult(10)

将为第一页创建以下 SQL:

SELECT * FROM table WHERE rownum <= 10;

将为第二页创建以下 SQL:

SELECT row_.*, rownum rownum_ 
FROM 
(
    SELECT * 
    FROM table
 ) row_ 
 WHERE rownum <= 20 WHERE rownum_ > 10;

第一个查询正确返回前 10 行,而第二个查询仅返回 4 个新行和 6 个已经在“第 1 页”中的行。所以 6 行完全丢失。

所以,我想,到底是什么?

4

4 回答 4

2

您可以创建自己的自定义 Oracle 方言来解决此问题,或者您可以等待补丁 NH-3814 被接受。最好等待 NHibernate 接受补丁,但如果你没有时间,你可以这样做。

https://nhibernate.jira.com/browse/NH-3814

此类将覆盖 Oracle8iDialect 中的一个方法,以允许 rownum_ 字段用于跳过和取值。我不得不使用反射来调用 ExtractColumnOrAliasNames,因为这是一个私有方法。

internal class Oracle10gDialectExtended : NHibernate.Dialect.Oracle10gDialect
{

    public Oracle10gDialectExtended()
    {
    }

    /// <summary>
    /// This is from patch NH-3814
    /// https://nhibernate.jira.com/browse/NH-3814
    /// https://nhibernate.jira.com/secure/attachment/24661/Oracle8iDialect.cs
    /// </summary>
    /// <param name="sql"></param>
    /// <param name="offset"></param>
    /// <param name="limit"></param>
    /// <returns></returns>
    public override NHibernate.SqlCommand.SqlString GetLimitString(NHibernate.SqlCommand.SqlString sql, NHibernate.SqlCommand.SqlString offset, NHibernate.SqlCommand.SqlString limit)
    {
        sql = sql.Trim();
        bool isForUpdate = false;
        if (sql.EndsWithCaseInsensitive(" for update"))
        {
            sql = sql.Substring(0, sql.Length - 11);
            isForUpdate = true;
        }

        string selectColumns = ExtractColumnOrAliasNames(sql);

        var pagingSelect = new SqlStringBuilder(sql.Count + 10);
        if (offset != null)
        {
            pagingSelect.Add("select " + selectColumns + " from ( select row_.*, rownum rownum_ from ( ");
        }
        else
        {
            pagingSelect.Add("select " + selectColumns + " from ( ");
        }

        pagingSelect.Add(sql);

        if (offset != null && limit != null)
        {
            pagingSelect.Add(" ) row_ ) where rownum_ <=").Add(limit).Add(" and rownum_ >").Add(offset);
        }
        else if (limit != null)
        {
            pagingSelect.Add(" ) where rownum <=").Add(limit);
        }
        else
        {
            // offset is specified, but limit is not.
            pagingSelect.Add(" ) row_ ) where rownum_ >").Add(offset);
        }

        if (isForUpdate)
        {
            pagingSelect.Add(" for update");
        }

        return pagingSelect.ToSqlString();
    }

    private string ExtractColumnOrAliasNames(SqlString select)
    {
        BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
        MethodInfo info = typeof(NHibernate.Dialect.Oracle8iDialect).GetMethod("ExtractColumnOrAliasNames", flags, null, new Type[] { typeof(SqlString) }, null);

        if (info == null)
        {
            throw new MissingMethodException("Method ExtractColumnOrAliasNames does not exist on class NHibernate.Dialect.Oracle8iDialect");
        }

        return info.Invoke(this, new object[] { select }) as string;
    }
}
于 2015-09-16T21:28:26.237 回答
0

NH 3.2 在 Oracle 上存在分页问题(类似的问题在这里)。我遇到了同样的问题并一直使用 3.1 直到它被修复。

于 2012-11-08T17:18:22.783 回答
0

也许我遗漏了一些东西,但我认为你想要这个作为你的第二个查询:

SELECT row_.*
FROM 
(
    SELECT *, rownum rn   --- place your rownum here
    FROM table
 ) row_ 
 WHERE rn > 10 
    and rn <=20

这适用于内部查询,然后您在子句rownum中选择行号为 11-20 的记录。WHERE

请注意,您应该ORDER BY在两个查询中都包含一个子句,以确保返回时记录的顺序正确。

于 2012-11-08T15:39:30.450 回答
-1

我认为 Powerslave 粗暴地指了他的示例,该示例最初包括order by并且类似于:

SELECT * FROM 
    (SELECT row_.*, rownum rownum_ FROM 
        (
            SELECT * 
            FROM table
            ORDER BY some_column
        ) row_ 
        WHERE rownum <= 20) 
    WHERE rownum_ > 10;

基本上发生的事情是 NHibernate 生成一个查询:

  1. 排序结果
  2. 将这些限制为前 20 个有序行
  3. 忘记下单了...
  4. ...然后采用无序结果 11-20。

最终结果是您将看到每页的正确行数,但有些页面的行会出现在其他页面上,并且也会出现缺失的行。我已经针对 Oracle 手动运行了 NHibernate (4.0.3) 生成的查询,以验证所有这些都是真的。

我已经在这里向 NHibernate 提交了错误报告和补丁。希望我的补丁是洁净的,他们会在下一个版本中修复这个问题。

于 2015-08-04T21:33:25.950 回答