Sabemos que es un dato (por ejemplo, el número 32 o la palabra "Juan"). ¿Qué es un metadato? Cualquier información al respecto del dato que nos ayuda a comprenderlo o utilizarlo. Los primeros metadatos de interés son aquellos que tienen que ver con la estructura de datos, como el nombre y tipo de una columna, por ejemplo, el 32 es uno de los datos de la columna 'edad', del tipo int, que está en la tabla 'cliente'. Pero esto es trivial, metadatos propiamente dichos son información sobre el origen de datos de la columna o tabla, una explicación de la columna o tabla, restricciones, la descripción del cálculo al que obedece, etc. Por ejemplo, la columna 'edad' puede tener como metadato una descripción del modo en que se ha calculado: 'fecha_actual - tabla1.fecha_nacimiento'.
El manejo de metadatos en JDBC tiene interés cuando tenemos que realizar aplicaciones que acceden a cualquier
base de datos o ejecuten cualquier sentencia dada por el usuario.
Ejecutar cualquier consulta (query)
Vamos a realizar una sencilla función que ejecuta cualquier SELECT que el usuario introduzca por teclado.En nuestro ejemplo tenemos que observar:
static String obt_sentencia() {
try {
/* Creo el objeto 'entrada', es un lector de entradas por teclado */
BufferedReader entrada = new BufferedReader(new InputStreamReader(System.in));
/****** Pido la sentencia por teclado ******/
System.out.println( "Teclee consulta:");
return entrada.readLine();
}
catch (IOException e) { e.printStackTrace(); return null; }
}
Statement s = con.createStatement();
boolean tiene_resultados = s.execute( sentencia );
if ( tiene_resultados )
(new consulta_x( con )).ver_resultados( s );
ResultSet rs = s.getResultSet( );
ResultSetMetaData mdata = rs.getMetaData(); // Obtener Metadatos
int num_columnas = mdata.getColumnCount(); // Obtener número de columnas
Después obtenemos los nombres de las columnas:
for ( int i = 1; i <= num_columnas; i++ ) {
if ( i > 1 )
System.out.print( ", " );
System.out.print( mdata.getColumnLabel( i ) ); // Mostrar nombres de campos
}
El ejemplo, con el código fuente completo es:
package jdbc01;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.lang.ClassNotFoundException;
import java.io.*;
public class metadatos {
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" );
/*** Obtiene la sentencia (String) por teclado ***/
String sentencia = obt_sentencia();
/*** Crea un objeto Statement ***/
Statement s = con.createStatement();
/*** Ejecutamos la sentencia: si hay resultados, los mostramos ***/
boolean tiene_resultados = s.execute( sentencia );
if ( tiene_resultados )
(new consulta_x( con )).ver_resultados( s ); // La clase consul_x obtiene metadatos y datos
s.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 );
}
}
static String obt_sentencia() {
try {
/* Creo el objeto 'entrada', es un lector de entradas por teclado */
BufferedReader entrada = new BufferedReader(new InputStreamReader(System.in));
/****** Pido la sentencia por teclado ******/
System.out.println( "Teclee consulta:");
return entrada.readLine();
}
catch (IOException e) { e.printStackTrace(); return null; }
}
/*************************************************************
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(); }
}
}
class consulta_x {
Connection con;
consulta_x( Connection con ) {
this.con = con;
}
/*** Muestra los resultados de cualquier sentencia (query) ***/
void ver_resultados( Statement s ) {
try {
ResultSet rs = s.getResultSet( );
ResultSetMetaData mdata = rs.getMetaData(); // Obtener Metadatos
int num_columnas = mdata.getColumnCount(); // Obtener número de columnas
/*** Mostrar nombres de columnas ****/
for ( int i = 1; i <= num_columnas; i++ ) {
if ( i > 1 )
System.out.print( ", " );
System.out.print( mdata.getColumnLabel( i ) ); // Mostrar nombres de campos
}
/*** Mostrar resultados. Fila a fila. Dentro de cada fila: columna a columna ****/
while (rs.next()) {
System.out.println( "" ); // Nueva línea antes de la fila
for ( int i = 1; i <= num_columnas; i++) { // Recorro las columnas
if ( i > 1 )
System.out.print( ", " );
System.out.print( rs.getString(i) );
}
}
}
catch (SQLException e) { e.printStackTrace(); }
}
}
Además podemos obtener el tamaño de cada columna por medio de:
int size = mdata.getColumnDisplaySize( int numero_columna );
Antes vimos que el manejo de metadatos en JDBC tiene interés cuando se tienen que ejecutar sentencias de las que se desconoce su estructura a priori. En este apartado vamos a ir a más: pudiera ocurrir que necesitemos acceder a información de la base de datos, tablas o incluso de las capacidades de nuestro controlador JDBC.
Para comprender el tipo de metadato que podemos conseguir, vamos a empezar por el final (por el resultado), mostrando los metadatos que obtiene nuestra aplicación, simplemente partiendo de la información de driver y base de datos. Lo que muestra la aplicación se dividide en dos bloques:
--------- CARACTERISTICAS GENERALES --------------- Nombre de BD: MySQL Versión de BD: 4.0.18-max-debug Nombre de controlador: MySQL-AB JDBC Driver Versión de controlador: mysql-connector-java-3.0.11-stable ( $Date: 2004/02/04 02:47:36 $, $Revision: 1.27.2.34 $ ) Versión mayor de controlador JDBC: 3 Versión menor de controlador JDBC: 0 Statements simultaneos: 0 Soporte SQL92: false Soporte de SCROLL_SENSITIVE: false Además actualiza BD: false --------- CARACTERISTICAS DE CADA TABLA ------------ TABLE_CAT: prueba, TABLE_SCHEM: null, TABLE_NAME: cliente, TABLE_TYPE: TABLE, REMARKS: codigo (Tipo Java:java.lang.String) (Tipo SQL:CHAR) (Size=10) nombre (Tipo Java:java.lang.String) (Tipo SQL:CHAR) (Size=20) ape1 (Tipo Java:java.lang.String) (Tipo SQL:CHAR) (Size=20) ape2 (Tipo Java:java.lang.String) (Tipo SQL:CHAR) (Size=20) edad (Tipo Java:java.lang.Integer) (Tipo SQL:LONG) (Size=11) TABLE_CAT: prueba, TABLE_SCHEM: null, TABLE_NAME: venta, TABLE_TYPE: TABLE, REMARKS: codigo (Tipo Java:java.lang.String) (Tipo SQL:CHAR) (Size=10) precio (Tipo Java:java.lang.Float) (Tipo SQL:FLOAT) (Size=12) coste (Tipo Java:java.lang.Float) (Tipo SQL:FLOAT) (Size=12)
En el bloque de información general es importante destacar las prestaciones:
En el bloque de información de cada tabla cabe destacar::
El dódigo fuente completo y comentado es:
package jdbc01;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.lang.ClassNotFoundException;
public class metadatos2 {
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" );
/* Obtener metadatos de base de datos */
DatabaseMetaData dbmd = con.getMetaData();
/**********
Obtengo el ResultSet de las tablas:
El tercer argumento puede especificar el nombre de la tabla
El cuarto argumento especifica los tipos (TABLE, VIEW, etc.). null: todos
*********/
ResultSet rs = dbmd.getTables( null, null, null, new String[]{"TABLE"} );
/**** Obtenemos el ResultSetMetaData a partir del anterior, además obt. núm. columnas ****/
ResultSetMetaData rsmd = rs.getMetaData();
int num_cols = rsmd.getColumnCount();
/**** Mostrar información general *****/
System.out.println( "--------- CARACTERISTICAS GENERALES ---------------" );
System.out.println( "Nombre de BD: " + dbmd.getDatabaseProductName() );
System.out.println( "Versión de BD: " + dbmd.getDatabaseProductVersion() );
System.out.println( "Nombre de controlador: " + dbmd.getDriverName() );
System.out.println( "Versión de controlador: " + dbmd.getDriverVersion() );
System.out.println( "Versión mayor de controlador JDBC: " + dbmd.getJDBCMajorVersion() );
System.out.println( "Versión menor de controlador JDBC: " + dbmd.getJDBCMinorVersion() );
System.out.println( "Statements simultaneos: " + dbmd.getMaxStatements() );
System.out.println( "Soporte SQL92: " + dbmd.supportsANSI92FullSQL() );
System.out.println( "Soporte de SCROLL_SENSITIVE: " +
dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE));
System.out.println( "Además actualiza BD: " +
dbmd.supportsResultSetConcurrency(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE));
System.out.println( "--------- CARACTERISTICAS DE CADA TABLA ------------" );
/******************
ResultSet para tratar cada tabla: la fila tiene las características
de la tabla (base de datos, esquema, nombre, tipo y descripción)
*****************/
while ( rs.next() ) {
/*** Recorremos las características de cada tabla ***/
for ( int i = 1; i <= num_cols; i++ ) {
if ( i > 1 )
System.out.print( ", " );
System.out.print( rsmd.getColumnLabel(i) + ": "); // Nombre de campo
System.out.print( rs.getString(i) ); // Valor de campo
} /// Fin del recorrido por las características de cada tabla
/*********** Mostrar columnas de la tabla *************/
mostrar_cols_tabla( con, rs.getString(3) ); // getString(3)==nombre_tabla
System.out.println( "" ); // Nueva línea después de terminar con tabla
} //// Fin del tratamiento de cada tabla
rs.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(); }
}
/********* Muestra campos (columnas) de la tabla (String nom_tabla) ********/
static void mostrar_cols_tabla( Connection con, String nom_tabla ) {
try {
/*** Ejecutar sentencia SELECT para la tabla ****/
Statement senten = con.createStatement();
ResultSet rs_sel = senten.executeQuery("SELECT * FROM " + nom_tabla );
/*** Obtenemos metadatos de la sentencia SELECT ***/
ResultSetMetaData mdata_sel = rs_sel.getMetaData(); // Obtener Metadatos de consulta
int num_cols_sel = mdata_sel.getColumnCount(); // Obtener número de columnas
/*** Recorremos cada columna para mostrar sus características ****/
for (int num_col_sel = 1; num_col_sel <= num_cols_sel; num_col_sel++) {
System.out.println("");
/* Mostrar nombre de columna */
System.out.print("\t" + mdata_sel.getColumnLabel(num_col_sel));
/* Mostrar tipo Java */
System.out.print(" (Tipo Java:" + mdata_sel.getColumnClassName(num_col_sel) +")");
/* Mostrar tipo SQL */
System.out.print(" (Tipo SQL:" + mdata_sel.getColumnTypeName(num_col_sel) +")");
/* Mostrar tamaño */
System.out.print(" (Size=" + mdata_sel.getColumnDisplaySize(num_col_sel) +")");
}
senten.close();
rs_sel.close();
}
catch (SQLException e) { e.printStackTrace(); }
}
}
>
Observar que podemos obtener diferentes clases de metadatos:
DatabaseMetaData dbmd = con.getMetaData();
dbmd.getMaxStatements()
ResultSet rs = dbmd.getTables( null, null, null, new String[]{"TABLE"} );
ResultSet rs_sel = senten.executeQuery("SELECT * FROM " + nom_tabla );
ResultSetMetaData mdata_sel = rs_sel.getMetaData(); // Obtener Metadatos de consulta
Sirve para acceder a información sobre las columnas. En el siguiente ejemplo se accede a información sobre nombre y tamaño de la columna:
System.out.print("\t" + mdata_sel.getColumnLabel(num_col_sel));
System.out.print(" (Size=" + mdata_sel.getColumnDisplaySize(num_col_sel) +")");
¿Cómo obtenemos todos los campos de una tabla? En la función mostrar_cols_tabla() la sentencia SQL "SELECT * ..." nos permite acceder a todos los campos de la tabla.