2012-06-16 30 views
5

當我在squeryl中創建一個查詢時,它返回一個Query [T]對象。查詢還沒有執行,當我迭代Query對象時(Query [T] extends Iterable [T])。Squeryl:顯式運行查詢

執行查詢時,必須有一個事務{}或一個inTransaction {}塊。

我只是說SELECT查詢和交易將不是必要的,但squeryl框架需要它們。

我想在我的應用程序的模型中創建一個查詢,並將其直接傳遞給模板中的視圖助手遍歷它並呈現數據的視圖。 這隻有在將事務{}塊放入控制器時纔有可能(控制器包含模板調用,因此迭代的模板也在其中)。無法將事務塊置於模型中,因爲該模型沒有真正執行查詢。

但在我的理解中,交易與控制器無關。這是數據庫框架使用模型的一個決定,如何使用它以及在哪裏使用事務。所以我希望事務{}塊位於模型中。

我知道我可以 - 而不是返回Query [T]實例 - 調用此Query [T]對象上的Iterable [T] .toList,然後返回創建的列表。然後整個查詢在模型中執行,一切都很好。但我不喜歡這種方法,因爲從數據庫請求的所有數據都必須緩存在這個列表中。我更喜歡將數據直接傳遞給視圖的方式。我喜歡MySql功能,在結果集很大時對它進行流式傳輸。

有什麼可能嗎?可能類似於函數Query [T] .executeNow(),它將請求發送到數據庫,能夠關閉事務,但仍然使用MySQL流式傳輸功能並在接收到(選定並因此固定的)結果集的其餘部分時它被訪問?由於結果集在查詢的時候是固定的,因此關閉事務不應該是一個問題。

+0

如果你發佈你的解決方案,你會發現一個有趣/令人驚訝的解決方案,這將是很好的。 –

回答

5

,我在這裏看到的普遍的問題是您嘗試以下兩種思路結合起來:

  • 數據的懶惰計算;這裏:數據庫結果

  • 隱藏了對計算完成後必須觸發的後處理操作的需求;位置:從您的控制器隱藏或查看數據庫會話必須被關閉

由於您的計算是懶惰,因爲你不一定要它執行到最後(這裏:遍歷整個結果集),沒有明顯的鉤子可能觸發後處理步驟。

您的調用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() 

創建對象時,即在IterableQuery的構造函數內,或者在將對象傳遞給控制器​​之前,已經可以調用manualIt.start()

但是,以這種方式處理資源(文件,數據庫連接等)非常脆弱,因爲在異常情況下不會觸發後處理。如果您查看org.squeryl.dsl.QueryDsl._using的實施情況,您將看到IterableQuery中缺少的try ... finally塊。