2012-06-16 9 views
5

Wenn ich eine Abfrage in Squeryl erstellen, wird ein Query [T] -Objekt zurückgegeben. Die Abfrage wurde noch nicht ausgeführt und wird ausgeführt, wenn ich über das Abfrageobjekt iteriere (Abfrage [T] erweitert Iterable [T]).Squeryl: Abfrage explizit ausführen

Um die Ausführung einer Abfrage muss entweder eine Transaktion {} oder ein inTransaction {} Block sein.

Ich spreche nur von SELECT-Abfragen und Transaktionen wäre nicht notwendig, aber das Squeryl-Framework benötigt sie.

Ich möchte eine Abfrage im Modell meiner Anwendung erstellen und direkt an die Ansicht übergeben, wo ein View-Helper in der Vorlage darüber iteriert und die Daten darstellt. Dies ist nur möglich, wenn Sie den Transaktionsblock {} in den Controller setzen (der Controller enthält den Aufruf der Vorlage, also die Vorlage, die die Iteration ebenfalls ausführt). Es ist nicht möglich, den Transaktionsblock {} in das Modell einzufügen, da das Modell die Abfrage nicht wirklich ausführt.

Aber in meinem Verständnis hat die Transaktion nichts mit dem Controller zu tun. Es ist eine Entscheidung des Modells, welches Datenbank-Framework zu verwenden ist, wie es zu verwenden ist und wo Transaktionen zu verwenden sind. Daher möchte ich, dass der Transaktionsblock {} im Modell enthalten ist.

Ich weiß, dass ich - anstatt die Query [T] -Instanz zurückzugeben - Iterable [T] .toList auf diesem Query [T] -Objekt aufrufen und dann die erstellte Liste zurückgeben kann. Dann wird die gesamte Abfrage im Modell ausgeführt und alles ist in Ordnung. Aber ich mag diesen Ansatz nicht, weil alle von der Datenbank angeforderten Daten in dieser Liste zwischengespeichert werden müssen. Ich würde einen Weg bevorzugen, wo diese Daten direkt an die Ansicht weitergegeben werden. Ich mag die MySql-Funktion zum Streamen der Ergebnismenge, wenn sie groß ist.

Gibt es eine Möglichkeit? Vielleicht etwas wie eine Funktion Query [T] .executeNow(), die die Anfrage an die Datenbank sendet, ist in der Lage, die Transaktion zu schließen, aber immer noch die MySQL-Streaming-Funktion und empfängt den Rest der (ausgewählten und damit festen) Ergebnismenge wann es ist zugegriffen? Da die Ergebnismenge im Moment der Abfrage festgelegt ist, sollte das Schließen der Transaktion kein Problem darstellen.

+0

Es wäre schön, wenn Sie Ihre Lösung veröffentlichen würden, sollten Sie eine interessante/überraschende finden. –

Antwort

5

Das allgemeine Problem, das ich hier sehe, ist, dass Sie versuchen, die folgenden zwei Ideen zu kombinieren:

  • faul Berechnung von Daten; hier: Datenbankergebnisse

  • Verbergen der Notwendigkeit für eine Nachbearbeitungsaktion, die ausgelöst werden muss, wenn die Berechnung erfolgt ist; hier: Verstecken von Ihrem Controller oder sehen Sie, dass die Datenbanksitzung

    geschlossen werden muss

Da Ihre Berechnung faul ist und da Sie nicht ausführen es bis zum Ende (hier verpflichtet: iterieren über die gesamte Ergebnismenge), gibt es keinen offensichtlichen Haken, der den Nachbearbeitungsschritt auslösen könnte.

Ihr Vorschlag zum Aufrufen von Query[T].toList zeigt dieses Problem nicht, da die Berechnung bis zum Ende ausgeführt wird und die Anforderung des letzten Elements der Ergebnismenge als Auslöser für das Schließen der Sitzung verwendet werden kann.

Das heißt, das Beste, was ich tun konnte die folgenden Ergebnisse, die eine Anpassung des Codes innerhalb org.squeryl.dsl.QueryDsl._using ist:

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 
    } 
} 

Clients die Abfrage-Wrapper wie folgt verwendet werden:

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() 

Der Aufruf von manualIt.start() konnte bereits beim Erstellen des Objekts erfolgen, dh innerhalb des Konstruktors IterableQuery oder bevor das Objekt an den Controller übergeben wird.

Allerdings ist das Arbeiten mit Ressourcen (Dateien, Datenbankverbindungen usw.) sehr fragil, da die Nachbearbeitung im Ausnahmefall nicht ausgelöst wird. Wenn Sie sich die Implementierung von org.squeryl.dsl.QueryDsl._using ansehen, werden Sie ein paar try ... finally Blöcke sehen, die von IterableQuery fehlen.