2012-06-16 11 views
5

Cuando creo una consulta en squeryl, devuelve un objeto Query [T]. La consulta aún no se ejecutó y será, cuando itere sobre el objeto Query (Query [T] extends Iterable [T]).Squeryl: ejecutar consulta explícitamente

Alrededor de la ejecución de una consulta tiene que haber una transacción {} o un bloque inTransaction {}.

Estoy hablando de consultas SELECT y las transacciones no serían necesarias, pero el marco de squeryl las necesita.

Me gustaría crear una consulta en el modelo de mi aplicación y pasarla directamente a la vista donde un asistente de visualización en la plantilla itera sobre ella y presenta los datos. Esto solo es posible cuando se coloca el bloque {} de transacción en el controlador (el controlador incluye la llamada de la plantilla, por lo que la plantilla que realiza la iteración también está dentro). No es posible poner el bloque {} de transacción en el modelo, porque el modelo realmente no ejecuta la consulta.

Pero a mi entender, la transacción no tiene nada que ver con el controlador. Es una decisión del modelo qué marco de base de datos usar, cómo usarlo y dónde usar las transacciones. Por lo tanto, quiero que el bloque transaction {} esté en el modelo.

Sé que puedo, en lugar de devolver la instancia de Query [T], invocar Iterable [T] .toList en este objeto Query [T] y luego devolver la lista creada. Entonces toda la consulta se ejecuta en el modelo y todo está bien. Pero no me gusta este enfoque, porque todos los datos solicitados de la base de datos deben almacenarse en caché en esta lista. Prefiero una forma en que estos datos se pasen directamente a la vista. Me gusta la función MySql de transmitir el conjunto de resultados cuando es grande.

¿Hay alguna posibilidad? Tal vez algo así como una función Query [T] .executeNow() que envía la solicitud a la base de datos, puede cerrar la transacción, pero aún utiliza la función de transmisión MySQL y recibe el resto del conjunto de resultados (seleccionado y por lo tanto fijo) cuando se accede? Debido a que el conjunto de resultados se soluciona en el momento de la consulta, el cierre de la transacción no debería ser un problema.

+0

Sería bueno que publicaras tu solución, en caso de que encuentres una interesante/sorprendente. –

Respuesta

5

El problema general que veo aquí es que intenta combinar las dos siguientes ideas:

  • cómputo perezoso de los datos; aquí: resultados de la base de datos

  • ocultando la necesidad de una acción de post-procesamiento que debe activarse cuando se realiza el cálculo; aquí: esconderse de su controlador o descubre que la sesión de base de datos debe estar cerrada

Debido a que su cálculo es perezoso y ya que no están obligados a realizar al final (aquí: para repetir todo el conjunto de resultados), no hay un gancho obvio que pueda desencadenar el paso de postproceso.

Su sugerencia de invocar Query[T].toList no presenta este problema, ya que el cálculo se realiza hasta el final, y la solicitud del último elemento del conjunto de resultados se puede utilizar como desencadenante para cerrar la sesión.

Dicho esto, lo mejor que podía llegar a decir lo siguiente, que es una adaptación del código dentro 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 
    } 
} 

Los clientes pueden utilizar la envoltura consulta como sigue:

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

La invocación de manualIt.start() ya podría hacerse cuando se crea el objeto, es decir, dentro del constructor de IterableQuery, o antes de que el objeto pase al controlador.

Sin embargo, trabajar con recursos (archivos, conexiones de bases de datos, etc.) de esta manera es muy frágil, porque el postprocesamiento no se desencadena en caso de excepciones. Si observas la implementación de org.squeryl.dsl.QueryDsl._using, verás un par de bloques try ... finally que faltan en IterableQuery.

Cuestiones relacionadas