5

squerylでクエリを作成すると、Query[T]オブジェクトが返されます。クエリはまだ実行されておらず、Queryオブジェクトを反復処理すると実行されます(Query[T]はIterable[T]を拡張します)。

クエリの実行の前後には、transaction{}またはinTransaction{}ブロックのいずれかが必要です。

SELECTクエリについて話しているだけで、トランザクションは必要ありませんが、squerylフレームワークにはそれらが必要です。

アプリケーションのモデルでクエリを作成し、それをビューに直接渡します。ビューでは、テンプレートのビューヘルパーがクエリを繰り返し処理してデータを表示します。これは、transaction {}ブロックをコントローラーに配置する場合にのみ可能です(コントローラーにはテンプレートの呼び出しが含まれているため、反復を実行するテンプレートも内部にあります)。モデルは実際にはクエリを実行しないため、transaction{}ブロックをモデルに配置することはできません。

しかし、私の理解では、トランザクションはコントローラーとは何の関係もありません。どのデータベースフレームワークを使用するか、どのように使用するか、どこでトランザクションを使用するかは、モデルの決定です。したがって、transaction{}ブロックをモデルに含める必要があります。

Query [T]インスタンスを返す代わりに、このQuery[T]オブジェクトでIterable[T] .toListを呼び出してから、作成されたリストを返すことができることを知っています。次に、クエリ全体がモデルで実行され、すべてが正常に実行されます。しかし、データベースから要求されたすべてのデータをこのリストにキャッシュする必要があるため、このアプローチは好きではありません。このデータをビューに直接渡す方法がいいと思います。結果セットが大きいときに結果セットをストリーミングするMySql機能が好きです。

可能性はありますか?たぶん、データベースにリクエストを送信し、トランザクションを閉じることができる関数Query [T] .executeNow()のようなものですが、それでもMySQLストリーミング機能を使用し、残りの(選択された、したがって修正された)結果セットを次の場合に受け取ります。アクセスしましたか?結果セットはクエリの瞬間に修正されるため、トランザクションを閉じることは問題にはなりません。

4

1 に答える 1

5

ここで私が目にする一般的な問題は、次の2つのアイデアを組み合わせようとしていることです。

  • データの怠惰な計算; ここ:データベースの結果

  • 計算が完了したときにトリガーする必要がある後処理アクションの必要性を隠します。ここで:コントローラーから非表示にするか、データベースセッションを閉じる必要があることを確認します

計算は怠惰であり、最後まで実行する義務がないため(ここでは、結果セット全体を反復処理するため)、後処理ステップをトリガーする可能性のある明らかなフックはありません。

計算は最後まで実行されるため、呼び出しの提案でQuery[T].toListはこの問題は発生しません。結果セットの最後の要素を要求すると、セッションを閉じるためのトリガーとして使用できます。

そうは言っても、私が思いつくことができる最善の方法は次のとおりです。これは、内部のコードを適応させたものですorg.squeryl.dsl.QueryDsl._using

class IterableQuery[T](val q: Query[T]) extends Iterable[T] {
  private var lifeCycleState: Int = 0
  private var session: Session = null
  private var prevSession: Option[Session] = None

  def start() {
    assert(lifeCycleState == 0, "Queries may not be restarted.")
    lifeCycleState = 1

    /* Create a new session for this query. */
    session = SessionFactory.newSession

    /* Store and unbind a possibly existing session. */
    val prevSession = Session.currentSessionOption
    if(prevSession != None) prevSession.get.unbindFromCurrentThread

    /* Bind newly created session. */
    session.bindToCurrentThread
  }

  def iterator = {
    assert(lifeCycleState == 1, "Query is not active.")
    q.toStream.iterator
  }

  def stop() {
    assert(lifeCycleState == 1, "Query is not active.")
    lifeCycleState = 2

    /* Unbind session and close it. */
    session.unbindFromCurrentThread
    session.close

    /* Re-bind previous session, if it existed. */
    if(prevSession != None) prevSession.get.bindToCurrentThread
  }
}

クライアントは、次のようにクエリラッパーを使用できます。

var manualIt = new IterableQuery(booksQuery)
manualIt.start()
manualIt.foreach(println)
manualIt.stop()
//      manualIt.foreach(println) /* Fails, as expected */

manualIt = new IterableQuery(booksQuery) /* Queries can be reused */
manualIt.start()
manualIt.foreach(b => println("Book: " + b))
manualIt.stop()

の呼び出しはmanualIt.start()、オブジェクトが作成されたとき、つまり、のコンストラクター内でIterableQuery、またはオブジェクトがコントローラーに渡される前に、すでに実行されている可能性があります。

ただし、例外が発生した場合に後処理がトリガーされないため、このような方法でリソース(ファイル、データベース接続など)を操作することは非常に脆弱です。の実装を見ると、から欠落しているブロックがorg.squeryl.dsl.QueryDsl._usingいくつかあります。try ... finallyIterableQuery

于 2012-06-30T10:07:04.310 に答える