2010-01-20 10 views
14

tengo un conjunto de filas de una base de datos, y me gustaría proporcionar una interfaz para girar a través de ellos como esto:Scala: La exposición de un conjunto de resultados JDBC a través de un generador (iterable)

def findAll: Iterable[MyObject] 

Donde no requerimos tener todas las instancias en la memoria a la vez. En C# puede crear fácilmente generadores como este usando yield, el compilador se encarga de convertir el código que recorre el conjunto de registros en un iterador (como invertirlo).

Mi código actual es el siguiente:

def findAll: List[MyObject] = { 
    val rs = getRs 
    val values = new ListBuffer[MyObject] 
    while (rs.next()) 
    values += new valueFromResultSet(rs) 
    values.toList 
} 

¿Hay alguna manera de que pudiera convertir esto a no almacenar todo el conjunto en la memoria? Tal vez podría utilizar un para la comprensión?

Respuesta

13

Pruebe extender Iterator en su lugar. Yo no lo he probado, pero algo como esto:

def findAll: Iterator[MyObject] = new Iterator[MyObject] { 
    val rs = getRs 
    override def hasNext = rs.hasNext 
    override def next = new valueFromResultSet(rs.next) 
} 

Esto debe almacenar rs cuando se llama, y ​​de lo contrario sólo será un envoltorio de luz para las llamadas a rs.

Si desea guardar los valores que recorre, consulte Stream.

+0

Voy a dar una oportunidad, thx Rex –

+0

Acabo de suponer que rs en realidad tiene un método "hasNext". Si no es así, debe almacenar en caché el siguiente resultado con una var (probablemente privada) dentro del iterador y hacer que hasNeNe decir si ese resultado en caché existe. –

+1

Sí, eso funciona, utilicé hasNext = rs.isLast. Un problema es que no tengo ningún mecanismo para cerrar el rs y la conexión. En mi código actual he envuelto el código (arriba) en un método de "uso" que me cierra las cosas. –

12

me encontré con el mismo problema y en base a las ideas anteriores que he creado la siguiente solución simplemente escribiendo una clase adaptador:

class RsIterator(rs: ResultSet) extends Iterator[ResultSet] { 
    def hasNext: Boolean = rs.next() 
    def next(): ResultSet = rs 
} 

Con este ejemplo se puede realizar mapa operaciones en el conjunto de resultados - que era mi intención personal:

val x = new RsIterator(resultSet).map(x => { 
    (x.getString("column1"), x.getInt("column2")) 
}) 

Añada una .toList con el fin de forzar la evaluación. Esto es útil si la conexión de la base de datos está cerrada antes de usar los valores. De lo contrario, recibirá un error que le indicará que no puede acceder al ResultSet después de que se cerró la conexión.

+0

Utilicé esta solución. ¡Muchas gracias! Tuve que llamar a List para usar los resultados. val externalKeys = resultSet.map {x => x.getString ("external_key") } .toList – JasonG

+0

Hay un problema con esto. Según la especificación para hasNext debe ejecutarse sin avanzar el conjunto de resultados.Por lo tanto, el almacenamiento en caché como lo sugiere @Rex Kerr o el uso de isLast (no estoy seguro del rendimiento) es una mejor opción allí. – Sohaib

6

Una manera más sencilla (idiomático) para lograr el mismo sería

Iterator.continually((rs.next(), rs)).takeWhile(_._1).map(r => valueFromResultSet(r._2)).toList 

Es necesario el .toList para forzar la evaluación, de lo contrario la colección subyacente será un arroyo y el conjunto de resultados puede ser cerrado antes de la evaluación se ha llevado a cabo .

Cuestiones relacionadas