2012-06-16 8 views
5

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

クエリの実行の前後にトランザクション{}またはinTransaction {}ブロックが存在する必要があります。

私はちょうどSELECTクエリについて言及していますが、トランザクションは必要ではありませんが、squerylフレームワークにはそれらが必要です。

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

私の理解では、トランザクションはコントローラとは関係ありません。どのデータベースフレームワークを使用するか、どのように使用するか、そしてどこでトランザクションを使用するかは、モデルの決定です。だからトランザクション{}ブロックをモデルに入れたい。

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

可能性はありますか? MySQLのストリーミング機能を使用し、残りの(選択された固定された)結果セットを受け取ると、データベースに要求を送信し、トランザクションを閉じることができる関数Query [T] .executeNow()アクセスしましたか?結果セットはクエリの瞬間に修正されるため、トランザクションの終了は問題ではありません。私はここを参照してください

+0

興味深い/驚くべきものを見つけたら、ソリューションを投稿すればいいと思います。 –

答えて

5

一般的な問題は、あなたは、次の2つのアイデアを組み合わせてみてくださいということです。

  • データの遅延計算。ここではデータベース結果

  • 計算が完了したときにトリガされなければならない後処理アクションの必要性が隠されています。ここではあなたのコントローラから隠れたり、計算が面倒なので、データベースセッションが

を閉じなければならないことを表示しているので、あなたはここで非常に最後(にそれを実行する義務はありません。全体の結果セットを反復します)、後処理ステップをトリガーする可能性のある明確なフックはありません。

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

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() 
を私が思い付くことが最高の org.squeryl.dsl.QueryDsl._using内のコードの適応である、以下れる、と述べた

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

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

関連する問題