Al hablar de las sentencias SELECT hemos visto lo que es un ResultSet: la forma de ancapsular o recoger los resultados (fila a fila) de una sentencia. Algo que puede resultar útil de un ResultSet es la capacidad para navegar o desplazarme por él. En los ejemplos anteriores hemos obtenido todas las filas (secuencialmente, una detrás de otra):
ResultSet rs = sentencia.executeQuery( orden_SQL );
/*** Recorrer fila a fila el resultado ****/
while ( rs.next() ) {
String res = rs.getString( "codigo" ) + ", " + rs.getString( "nombre" ) + ", " + rs.getInt( "edad" );
System.out.println( res );
}
Pudiera ocurrir que nos interese tratar de forma más selectiva el conjunto; por ejemplo, supongamos que queremos paginarlo, es decir, presentar al usuario subconjuntos o páginas del ResultSet (como hace un buscador, que te página los resultados). Pudiera ocurrir que nos interese pasar directamente a la última fila sin tener que pasar por todas las intermedias. O tal vez volver de nuevo a empezar desde el primero, sin necesidad de reejecutar la sentencia. Estas son algunas de las ventajas de los conjuntos de resultados con desplazamiento o navegación.
Antes de pasar a mayores conviene tener claro el concepto de "cursor activo" o, como se dice usualmente, simplemente "cursor". El cursor es la posición activa o actual del ResulSet. Un ResultSet no puede "ver" todos las filas simultanemente, necesita una herramienta para saber cual es la fila que está tratando en un momento dado, esto es el cursor, una forma de apuntar o señalar a la posición activa o actual. De esta manera una llamada a un método getXXX() sabe en todo momento a que fila se refiere la petición de datos. Hay una particularidad, al ejecutar la sentencia executeQuery el cursor se sitúa en una pseudofila: la anterior a la primera, entonces decimos que el conjunto "apunta" a la posición anterior a la primera fila. Por esta razón para obtener la primera fila lo primero que tenemos que hacer es llamar a next(), es decir, pedirle al conjunto que adelante el cursor una posición, a la primera fila "real":
/*** Recorrer fila a fila el resultado ****/
while ( rs.next() ) {
....
}
La operativa es tan sencilla que el código siguiente casi se comenta sólo:
/************************************************************************
Ejemplo de ResultSet con desplazamiento
*************************************************************************/
public class rs_desplazamiento {
/**** Columnas del ResultSet ****/
static String col1 = "codigo";
static String col2 = "nombre";
public static void main(String[] args) {
Connection con = null;
try {
/*** Registro de driver ****/
Class.forName("com.mysql.jdbc.Driver");
/*** Crear conexión con base de datos ***/
con = DriverManager.getConnection( "jdbc:mysql://localhost/prueba", "root", "palabra" );
/***** Definir sentencia y ejecutarla ********/
String orden_SQL = "SELECT " + col1 + "," + col2 + " FROM cliente ORDER BY " + col2;
Statement sentencia = con.createStatement();
ResultSet rs = sentencia.executeQuery( orden_SQL );
/*** Recorrer fila a fila todo el resultado ****/
while ( rs.next() )
System.out.println( rs.getString( col1 ) + ", " + rs.getString( col2 ) );
/*** Nos ponemos en el primero y lo imprimimos ***/
rs.first();
System.out.println( "PRIMERO: " + rs.getString( col1 ) + ", " + rs.getString( col2 ) );
/*** Nos ponemos en el último y lo imprimimos ***/
rs.last();
System.out.println( "ULTIMO: " + rs.getString( col1 ) + ", " + rs.getString( col2 ) );
/*** Nos ponemos en el antepenúltimo y lo imprimimos ***/
rs.relative(-2);
System.out.println( "PENULTIMO: " + rs.getString( col1 ) + ", " + rs.getString( col2 ) );
/*** Volvemos a recorrer fila a fila todo el resultado ****/
rs.beforeFirst(); // Ojo: si no lo pongo, no comienzo en el primero
while ( rs.next() )
System.out.println( rs.getString( col1 ) + ", " + rs.getString( col2 ) );
sentencia.close();
}
catch( ClassNotFoundException e ) { e.printStackTrace(); }
catch (SQLException e) { e.printStackTrace(); }
/*** Haya excepción o no, tengo que cerrar la conexión ***/
finally {
cerrar_conexion( con );
}
}
/********************************************************************
Me aseguro de que se cierra la conexión
********************************************************************/
public static void cerrar_conexion( Connection con ) {
try {
if ( con != null )
if ( !con.isClosed() ) // Si no está cerrada, la cierro
con.close();
}
catch (SQLException e) { e.printStackTrace(); }
}
}
En el ejemplo que acabamos de ver first(), last() o relative() son autoevidentes. Son formas de desplzarnos por un conjunto de resultados de una sentencia. OJO: NAVEGAMOS POR UN CONJUNTO DE RESULTADOS DE UNA SENTENCIA, NO POR LA TOTALIDAD DE LA TABLA. Lo que ocurre es que en nuestro caso no tenemos ninguna claúsula WHERE y, por ello, las filas de la sentencia coinciden con las filas de la tabla (pero no siempre va a ser así).
Hay un aspecto que puede resultar de interes observar: en nuestro ejemplo la última operación es volver a recorrer todo el conjunto dede el principio, para ello, antes de usar next() debemos poner el cursor en la pseudofila, la posición anterior a la primera:
/*** Volvemos a recorrer fila a fila todo el resultado ****/ rs.beforeFirst(); // Ojo: si no lo pongo, no comienzo en el primero while ( rs.next() ) System.out.println( rs.getString( col1 ) + ", " + rs.getString( col2 ) );
Todos los métodos (tanto los anteriores como los que vamos a ver a continuación, a excepción de getRow) devuelven false en caso de que el desplazamiento se haga fuera del ResultSet (a una pseudofila). No hay desplazamintos hacia atras si el cursor apunta a la posición anterior a la primera fila. De forma semejante no hay desplazamiento hacia adelante si el cursor apunta a la posición posterior a la última.
Otros métodos:
Los ResulSet pueden tener o no desplazamento y pueden ser sensibles o no a los cambios en la base de datos que pueden producir otros usuarios o procesos. Tipos de ResultSet:
| TYPE_FORWARD_ONLY | Sólo se puede recorrer hacia adelante y no es sensible a cambios en base de datos. |
| TYPE_SCROLL_INSENSITIVE | Tiene desplazamiento (adelante/atrás), pero es insensible a cambios en base de datos. |
| TYPE_SCROLL_SENSITIVE | Tiene desplazamiento (adelante/atrás) y es sensible a cambios en base de datos. |
Con el siguiente código comprobamos que el ResultSet tenga desplazamiento:
DatabaseMetaData dbmd = con.getMetaData();
if ( dbmd.supportsResultSetType( ResultSet.TYPE_FORWARD_ONLY ) )
System.out.println("Esta base de datos no admite scroll");
else
System.out.println("Esta base de datos admite scroll");
Además hay una versión de createStatement que dispará una excepción del tipo SQLException, en el caso de que la base de datos no permita el tipo señalado en ( resultSetType ):
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
También podemos saber el tipo de un conjunto mediante un método de ResulSet:
public int getType()
Que devuelve el tipo en la forma de las constantes antes descritas: TYPE_FORWARD_ONLY, etc.