1

現在、Oracle 10g と NHibernate を使用してデータをページングしているときに、アプリケーションで奇妙な動作に遭遇しました。

20 行のテーブルがあり、サイトごとに 10 行をリストに表示したいので、リストには 2 ページあります。

NHibernate を使用して、最初のページquery.SetMaxResults(10).SetFirstResult(0)と 2 番目のページを設定していますquery.SetMaxResults(10).SetFirstResult(10)

次の SQL が最初のページに作成されます。

SELECT * FROM table WHERE rownum <= 10;

次の SQL が 2 ページ目に作成されます。

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

最初のクエリは最初の 10 行を正しく返しますが、2 番目のクエリは新しい 4 行と "ページ 1" に既に存在する 6 行のみを返します。したがって、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

何かが足りないかもしれませんが、2番目のクエリでこれが必要だと思います。

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 に答える
0

NH 3.2 では、Oracle のページネーションに問題があります (同様の質問はこちら)。私は同じ問題を抱えていて、修正されるまで 3.1 を使い続けました。

于 2012-11-08T17:18:22.783 に答える
-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 を取得します。

最終的には、すべてのページで正しい行数が表示されますが、一部のページには他のページに表示される行があり、欠落している行もあります。NHibernate (4.0.3) で生成されたクエリを手動で Oracle に対して実行し、これらすべてが正しいことを確認しました。

NHibernate へのバグ レポートとパッチの両方をここに提出しました。うまくいけば、私のパッチはコーシャであり、次のリリースで修正されるでしょう.

于 2015-08-04T21:33:25.950 に答える