Mover el Cursor en una Hoja de Resultados

Una de las nuevas características del API JDBC 2.0 es la habilidad de mover el cursor en una hoja de resultados tanto hacia atrás como hacia adelante. También hay métodos que nos permiten mover el cursor a una fila particular y comprobar la posición del cursor. La hoja de resultados Scrollable hace posible crear una herramienta GUI (Interface Gráfico de Usuario) para navegar a través de ella, lo que probablemente será uno de los principales usos de esta característica. Otro uso será movernos a una fila para actualizarla.

Antes de poder aprovechar estas ventajas, necesitamos crear un objeto ResultSet Scrollable:

Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
				     ResultSet.CONCUR_READ_ONLY);
ResultSet srs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES");
Este código es similar al utilizado anteriormente, excepto en que añade dos argumentos al método createStatement. El primer argumento es una de las tres constantes añadidas al API ResultSet para indicar el tipo de un objeto ResultSet: TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE, y TYPE_SCROLL_SENSITIVE. El segundo argumento es una de las dos constantes de ResultSet para especificar si la hoja de resultados es de sólo lectura o actualizable:CONCUR_READ_ONLY y CONCUR_UPDATABLE. Lo que debemos recordar aquí es que si especificamos un tipo, también debemos especificar si es de sólo lectura o actualizable. También, debemos especificar primero el tipo, y como ambos parámetros son int, el compilador no comprobará si los hemos intercambiado.

Especificando la constante TYPE_FORWARD_ONLY se crea una hoja de resultados no desplazable, es decir, una hoja en la que el cursor sólo se mueve hacia adelante. Si no se especifican constantes para el tipo y actualización de un objeto ResultSet, obtendremos automáticamente una TYPE_FORWARD_ONLY y CONCUR_READ_ONLY (exactamente igual que en el API del JDBC 1.0).

Obtendremos un objeto ResultSet desplazable si utilizamos una de estas constantes:TYPE_SCROLL_INSENSITIVE o TYPE_SCROLL_SENSITIVE. La diferencia entre estas dos es si la hoja de resultados refleja los cambios que se han hecho mientras estaba abierta y si se puede llamar a ciertos métodos para detectar estos cambios. Generalmente hablando, una hoja de resultados TYPE_SCROLL_INSENSITIVE no refleja los cambios hechos mientras estaba abierta y en una hoja TYPE_SCROLL_SENSITIVE si se reflejan. Los tres tipos de hojas de resultados harán visibles los resultados si se cierran y se vuelve a abrir. En este momento, no necesitamos preocuparnos de los puntos delicados de las capacidades de un objeto ResultSet, entraremos en más detalle más adelante. Aunque deberíamos tener en mente el hecho de que no importa el tipo de hoja de resultados que especifiquemos, siempre estaremos limitados por nuestro controlador de base de datos y el driver utilizados.

Una vez que tengamos un objeto ResultSet desplazable, srs en el ejemplo anterior, podemos utilizarlo para mover el cursor sobre la hoja de resultados. Recuerda que cuando creabamos un objeto ResultSet anteriormente, tenía el cursor posicionado antes de la primera fila. Incluso aunque una hoja de resultados se seleccione desplazable, el cursor también se posiciona inicialmente delante de la primera fila. En el API JDBC 1.0, la única forma de mover el cursor era llamar al método next. Este método todavía es apropiado si queremos acceder a las filas una a una, yendo de la primera fila a la última, pero ahora tenemos muchas más formas para mover el cursor.

La contrapartida del método next, que mueve el cursor una fila hacia delante (hacia el final de la hoja de resultados), es el nuevo método previous, que mueve el cursor una fila hacia atrás (hacia el inicio de la hoja de resultados). Ambos métodos devuelven false cuando el cursor se sale de la hoja de resultados (posición antes de la primera o después de la última fila), lo que hace posible utilizarlos en un bucle while. Ye hemos utilizado un método next en un bucle while, pero para refrescar la memoria, aquí tenemos un ejemplo que mueve el cursor a la primera fila y luego a la siguiente cada vez que pasa por el bucle while. El bucle termina cuando alcanza la última fila, haciendo que el método next devuelva false. El siguiente fragmento de código imprime los valores de cada fila de srs, con cinco espacios en blanco entre el nombre y el precio:

Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet srs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES");
while (srs.next()) {
	String name = srs.getString("COF_NAME");
	float price = srs.getFloat("PRICE");
	System.out.println(name + "     " + price);
}
La salida se podría parecer a esto:
Colombian     7.99
French_Roast     8.99
Espresso     9.99
Colombian_Decaf     8.99
French_Roast_Decaf     9.99
Al igual que en el fragmento anterior, podemos procesar todas las filas de srs hacia atrás, pero para hacer esto, el cursor debe estar detrás de la última fila. Se puede mover el cursor explícitamente a esa posicón con el método afterLast. Luego el método previous mueve el cursor desde la posicón detrás de la última fila a la última fila, y luego a la fila anterior en cada iteracción del bucle while. El bucle termina cuando el cursor alcanza la posición anterior a la primera fila, cuando el método previous devuelve false.
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, 					     ResultSet.CONCUR_READ_ONLY);
ResultSet srs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES");
srs.afterLast();
while (srs.previous()) {
	String name = srs.getString("COF_NAME");
	float price = srs.getFloat("PRICE");
	System.out.println(name + "     " + price);
}
La salida se podría parecer a esto:
French_Roast_Decaf     9.99
Colombian_Decaf     8.99
Espresso     9.99
French_Roast     8.99
Colombian     7.99
Como se puede ver, las dos salidas tienen los mismos valores, pero las filas están en orden inverso.

Se puede mover el cursor a una fila particular en un objeto ResultSet. Los métodos first, last, beforeFirst, y afterLast mueven el cursor a la fila indicada en sus nombres. El método absolute moverá el cursor al número de fila indicado en su argumento. Si el número es positivo, el cursor se mueve al número dado desde el principio, por eso llamar a absolute(1) pone el cursor en la primera fila. Si el número es negativo, mueve el cursor al número dado desde el final, por eso llamar a absolute(-1) pone el cursor en la última fila. La siguiente línea de código mueve el cursor a la cuarta fila de srs:

srs.absolute(4);
Si srs tuviera 500 filas, la siguiente línea de código movería el cursor a la fila 497:
srs.absolute(-4);
Tres métodos mueven el cursor a una posición relativa a su posición actual. Como hemos podido ver, el método next mueve el cursor a la fila siguiente, y el método previous lo mueve a la fila anterior. Con el método relative, se puede especificar cuántas filas se moverá desde la fila actual y también la dirección en la que se moverá. Un número positivo mueve el cursor hacia adelante el número de filas dado; un número negativo mueve el cursor hacia atrás el número de filas dado. Por ejemplo, en el siguiente fragmente de código, el cursor se mueve a la cuarta fila, luego a la primera y por último a la tercera:
srs.absolute(4); // cursor está en la cuarta fila
. . . 
srs.relative(-3); // cursor está en la primera fila
. . . 
srs.relative(2); // cursor está en la tercera fila
El método getRow permite comprobar el número de fila donde está el cursor. Por ejemplo, se puede utilizar getRow para verificar la posición actual del cursor en el ejemplo anterior:
srs.absolute(4); 
int rowNum = srs.getRow(); // rowNum debería ser 4
srs.relative(-3); 
int rowNum = srs.getRow(); // rowNum debería ser 1
srs.relative(2); 
int rowNum = srs.getRow(); // rowNum debería ser 3
Existen cuatro métodos adicionales que permiten verificar si el cursor se encuentra en una posición particular. La posición se indica en sus nombres:isFirst, isLast, isBeforeFirst, isAfterLast. Todos estos métodos devuelven un boolean y por lo tanto pueden ser utilizados en una sentencia condicional. Por ejemplo, el siguiente fragmento de código comprueba si el cursor está después de la última fila antes de llamar al método previous en un bucle while. Si el método isAfterLast devuelve false, el cursor no estará después de la última fila, por eso se llama al método afterLast. Esto garantiza que el cursor estára después de la última fila antes de utilizar el método previous en el bucle while para cubrir todas las filas de srs.
if (srs.isAfterLast() == false) {
	srs.afterLast();	
}
while (srs.previous()) {
	String name = srs.getString("COF_NAME");
	float price = srs.getFloat("PRICE");
	System.out.println(name + "     " + price);
}
En la siguiente página, veremos cómo utilizar otros dos métodos de ResultSet para mover el cursor, moveToInsertRow y moveToCurrentRow. También veremos ejemplos que ilustran por qué podríamos querer mover el cursor a ciertas posiciones.

Ozito