JAVA JDBC: ResultSet to List

JAVA JDBC: ResultSet to List
INTRODUCCIÓN
Cuando desarrollamos una aplicación con JDBC, una de las tareas que programamos frecuentemente es pasar un objeto de tipo ResultSet a una lista de objetos Map, List<Map<String,Object>>.
Por ejemplo, cuando queremos hacer una consulta a varias tablas, luego el resultado se debe mostrar en una tabla o quisas en un reporte con Jasper Report.
En este oportunidad desarrollara la clase JdbcUtil con un método que permite pasar un objeto ResultSet a una lista, la base de datos a utilizar es EurekaBank.
Las pruebas se realizan con 3 motores de bases de datos diferentes: Oracle, MySQL y SQL Server.
La base de datos la puede descargar desde:
METADATA DEL ResultSet
Para acceder a la metadata de un objeto ResultSet se utiliza el metodo getMetaData(), para obtener la cantidad de columnas se utiliza el método getColumnCount() de la metadata, para obtener el nombre de la columna se utiliza el método getColumnName(i) de la metadata, para obtener la etiqueta de la columna se utiliza el método getColumnLabel(i) de la metadata, siendo i el número de la columna e inicia en 1.
PRUEBA CON ORACLE
La versión de Oracle utilizada es la 11.2 y el driver jdbc utilizado es ojdbc6.jar.:
SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Express Edition Release 11.2.0.2.0 - Production
PL/SQL Release 11.2.0.2.0 - Production
CORE    11.2.0.2.0      Production
TNS for 32-bit Windows: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production
El código base a utilizar es:
// Datos Oracle
String driver = "oracle.jdbc.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:XE";
String user = "eureka";
String pass = "admin";
// Conexión
Class.forName(driver).newInstance();
cn = DriverManager.getConnection(url, user, pass);
// Consulta
String sql = "aquí debe ir la consulta";
PreparedStatement pstm = cn.prepareStatement(sql);
ResultSet rs = pstm.executeQuery();
// Obtener metadata
ResultSetMetaData md = rs.getMetaData();
int columns = md.getColumnCount();
System.out.println("getColumnName()\tgetColumnLabel()");
for (int i = 1; i <= columns; i++) {
  System.out.println(md.getColumnName(i) + "\t" + md.getColumnLabel(i));
}
rs.close();
pstm.close();
Consultando un tabla
La consulta a utilizar es la siguiente:
select 
  chr_cliecodigo, vch_cliepaterno, 
  vch_cliematerno, vch_clienombre 
from cliente
El resultado es el siguiente:
getColumnName()  getColumnLabel()
CHR_CLIECODIGO   CHR_CLIECODIGO
VCH_CLIEPATERNO  VCH_CLIEPATERNO
VCH_CLIEMATERNO  VCH_CLIEMATERNO
VCH_CLIENOMBRE   VCH_CLIENOMBRE
Cambiando el titulo de las columnas
La consulta a utilizar es la siguiente:
select 
  chr_cliecodigo cod, vch_cliepaterno pat, 
  vch_cliematerno mat, vch_clienombre nom
from cliente
El resultado es el siguiente:
getColumnName()   getColumnLabel()
COD               COD
PAT               PAT
MAT               MAT
NOM               NOM
Consultando dos tablas
La consulta a utilizar es la siguiente:
select 
  tm.vch_tipodescripcion tipo,
  sum(m.dec_moviimporte) importe
from tipomovimiento tm join movimiento m
on tm.chr_tipocodigo = m.chr_tipocodigo
group by tm.vch_tipodescripcion
El resultado es el siguiente:
getColumnName()   getColumnLabel()
TIPO              TIPO
IMPORTE           IMPORTE
De los resultados obtenidos se puede concluir que tanto getColumnName() y getColumnLabel() arrojan los mismos valores, se puede utilizar cualquiera de los dos métodos.
PRUEBA CON MYSQL
La versión de MySQL utilizada es la 5.6 y el driver jdbc utilizado es la versión 5.1.23:
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.12-log MySQL Community Server (GPL)

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective owners.
El código base a utilizar es:
// Datos MySQL
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/EUREKABANK";
String user = "eureka";
String pass = "admin";
// Conexión
Class.forName(driver).newInstance();
cn = DriverManager.getConnection(url, user, pass);
// Consulta
String sql = "aquí debe ir la consulta";
PreparedStatement pstm = cn.prepareStatement(sql);
ResultSet rs = pstm.executeQuery();
// Obtener metadata
ResultSetMetaData md = rs.getMetaData();
int columns = md.getColumnCount();
System.out.println("getColumnName()\tgetColumnLabel()");
for (int i = 1; i <= columns; i++) {
  System.out.println(md.getColumnName(i) + "\t" + md.getColumnLabel(i));
}
rs.close();
pstm.close();
Consultando un tabla
La consulta a utilizar es la siguiente:
select 
  chr_cliecodigo, vch_cliepaterno, 
  vch_cliematerno, vch_clienombre 
from cliente
El resultado es el siguiente:
getColumnName()   getColumnLabel()
chr_cliecodigo    chr_cliecodigo
vch_cliepaterno   vch_cliepaterno
vch_cliematerno   vch_cliematerno
vch_clienombre    vch_clienombre
Cambiando el titulo de las columnas
La consulta a utilizar es la siguiente:
select 
  chr_cliecodigo cod, vch_cliepaterno pat, 
  vch_cliematerno mat, vch_clienombre nom
from cliente
El resultado es el siguiente:
getColumnName()    getColumnLabel()
chr_cliecodigo     cod
vch_cliepaterno    pat
vch_cliematerno    mat
vch_clienombre     nom
Consultando dos tablas
La consulta a utilizar es la siguiente:
select 
  tm.vch_tipodescripcion tipo,
  sum(m.dec_moviimporte) importe
from tipomovimiento tm join movimiento m
on tm.chr_tipocodigo = m.chr_tipocodigo
group by tm.vch_tipodescripcion
El resultado es el siguiente:
getColumnName()        getColumnLabel()
vch_tipodescripcion    tipo
importe                importe
De los resultados obtenidos se puede concluir que getColumnLabel() sería la opción mas conveniente a utilizar.
PRUEBA CON SQL SERVER
La versión de SQL Server utilizada es la 2008 R2 y el driver jdbc utilizado es JTDS versión 1.2.5:
Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64) 
Apr  2 2010 15:48:46 
Copyright (c) Microsoft Corporation
Enterprise Edition (64-bit) on Windows NT 6.1  (Build 7601: Service Pack 1)
El código base a utilizar es:
// Datos SQL Server
String driver = "net.sourceforge.jtds.jdbc.Driver";
String url = "jdbc:jtds:sqlserver://localhost:1433/EUREKABANK";
String user = "sa";
String pass = "sql";
// Conexión
Class.forName(driver).newInstance();
cn = DriverManager.getConnection(url, user, pass);
// Consulta
String sql = "aquí debe ir la consulta";
PreparedStatement pstm = cn.prepareStatement(sql);
ResultSet rs = pstm.executeQuery();
// Obtener metadata
ResultSetMetaData md = rs.getMetaData();
int columns = md.getColumnCount();
System.out.println("getColumnName()\tgetColumnLabel()");
for (int i = 1; i <= columns; i++) {
  System.out.println(md.getColumnName(i) + "\t" + md.getColumnLabel(i));
}
rs.close();
pstm.close();
Consultando un tabla
La consulta a utilizar es la siguiente:
select 
  chr_cliecodigo, vch_cliepaterno, 
  vch_cliematerno, vch_clienombre 
from cliente
El resultado es el siguiente:
getColumnName()    getColumnLabel()
chr_cliecodigo     chr_cliecodigo
vch_cliepaterno    vch_cliepaterno
vch_cliematerno    vch_cliematerno
vch_clienombre     vch_clienombre
Cambiando el titulo de las columnas
La consulta a utilizar es la siguiente:
select 
  chr_cliecodigo cod, vch_cliepaterno pat, 
  vch_cliematerno mat, vch_clienombre nom
from cliente
El resultado es el siguiente:
getColumnName()    getColumnLabel()
cod                cod
pat                pat
mat                mat
nom                nom
Consultando dos tablas
La consulta a utilizar es la siguiente:
select 
  tm.vch_tipodescripcion tipo,
  sum(m.dec_moviimporte) importe
from tipomovimiento tm join movimiento m
on tm.chr_tipocodigo = m.chr_tipocodigo
group by tm.vch_tipodescripcion
El resultado es el siguiente:
getColumnName()        getColumnLabel()
vch_tipodescripcion    tipo
importe                importe
De los resultados obtenidos se puede concluir que ambos metodos arrojan los mismos resultados, por lo tanto, se puede utilizar cualquiera de los dos.
CLASE UTILITARIA
A continuación tenemos la clase propuesta para pasar un objeto ResultSet a una List<Map<String,?>>.
package pe.egcc.app.util;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Eric Gustavo Coronel Castillo
 * @blog gcoronelc.blogspot.com
 */
public final class JdbcUtil {

  private JdbcUtil() {
  }

  public static List<Map<String, ?>> rsToList(ResultSet rs) throws SQLException {
    ResultSetMetaData md = rs.getMetaData();
    int columns = md.getColumnCount();
    List<Map<String, ?>> results = new ArrayList<Map<String, ?>>();
    while (rs.next()) {
      Map<String, Object> row = new HashMap<String, Object>();
      for (int i = 1; i <= columns; i++) {
        row.put(md.getColumnLabel(i).toLowerCase(), rs.getObject(i));
      }
      results.add(row);
    }
    return results;
  }

}
A continuación se tiene un ejemplo de como utilizar esta clase:
 
List<Map<String,?>> lista = JdbcUtil.rsToList(rs);
 
Siendo rs un objeto ResultSet.
Puede descargar el proyecto desde el siguiente enlace:
Descargar Proyecto
CÓDIGO FUENTE
En esta sección te presento un video que una aplicación CLIENTE-SERVIDOR desarrollada con Java y Oracle.
Tú tienes acceso al código fuente de esta aplicación, después del video tienes el enlace.