JDBC: campos Blob y Cblob


Introducción

Citando a wikipedia, los BLOB (Binary Large OBjects, grandes objetos binarios) son elementos utilizados en las bases de datos para almacenar datos de gran tamaño que cambian de forma dinámica. No todos los SGBD son compatibles con los BLOB. El tipo de dato y su definición se introdujeron para representar datos que anteriormente no estaban definidos en las bases de datos para computadoras, pero que se hicieron posible al abaratarse los discos de almacenamiento.

Estos campos suelen ser los más dependientes del fabricante. Por ejemplo, en MySql 5.1 tenemos:

Hay además campos CLOB (basados en grandes cantidades de caracteres), que en MySql se denominan TEXT.

En Java contamos con tipos definidos en el paquete java.sql, como por ejemplo Cblob o Bblob. En los siguientes ejemplos vamos a trabajar con una tabla ('investigador') que contiene, entre otros, los siguientes campos:

Puede ver información sobre los tipos en MySql.

Select

En el siguiente ejemplo hacemos una consulta de todos los investigadores, accediendo al curriculum por medio de rs.getBlob("curriculum") y a las observaciones por medio de rs.getString("observaciones"). Existe uns clase que representa a la tabla 'investigador', se llama Investigador. En este ejemplo se devuelve un array de objetos de dicha clase:


    /************************************************************************************
    Un sencillo SELECT de investigadores, ordenados por apellidos y nombre
   **************************************************************************************/
   public static ArrayList select() {
		ArrayList array = null;
		Connection con = null;
		try {

			con = AccesoDatos.getConexion();
			array = new ArrayList();

			String orden_SQL = "SELECT nombre, apellido1, apellido2, nif, nacimiento, email, dedicacion, " +
		    					"curriculum, observaciones FROM investigador ORDER BY apellido1, apellido2,nombre";
			Statement sentencia = con.createStatement();
			ResultSet rs = sentencia.executeQuery( orden_SQL );
			Investigador inv;

			//// Recorrer fila a fila el resultado
			while ( rs.next() ) {
				inv = new Investigador( rs.getString( "nombre" ), rs.getString( "apellido1" ),
		    								rs.getString( "apellido2" ), rs.getString( "email" ), rs.getDate("nacimiento"),
		    								rs.getString( "nif" ), rs.getInt("dedicacion"), rs.getBlob("curriculum"),
		    								rs.getString("observaciones"));
				array.add( inv );
			}
			sentencia.close();
		}
		catch (SQLException e) { e.printStackTrace();  }
		AccesoDatos.cerrarConexion(con);
		return array;
   }

Este ejemplo tiene la virtud de ser sencillo, pero puede ser muy costoso en memoria. Resulta más habitual hacer una consulta de un único registro y almacenar los objetos LOB en un archivo temporal. A continuación podriamos permitir que el usuario se descargase el archivo. Esto es lo que se muestra en el siguiente ejemplo.



Select de un registro para generar archivo

Vamos a buscar un registro por medio de su clave primaria (el NIF en este caso) y volcar el contenido de los LOB en archivos situados en la carpeta que viene representada por la variable 'directorio'. Los argumentos segundo y tercero indican los nombres de los archivos, uno para las observaciones y otro para el curriculum:

Para volcar el contenido de los campos LOB usamos FileOutputStream. Para escribir en este stream podemos usar un array de bytes, que conseguimos con la llamada getBytes(nombre_columna).

Hay un detalle final: una vez cerrados los streams comprobamos que efectivamente existen los archivos mediante la llamada exist() de la clase File.


   /************************************************************************************
   Select de una fila (buscamos por NIF) que guarda en archivos (argumentos) las observaciones y cv
   Devuelve:
   -1: registro no encontrado
   0: error de io
   1: ok
  **************************************************************************************/
  public static int selectArchivos(String strNIF, String strArchivoObs, String strArchivoCv) {
		int resultado = 1;
		Connection con = null;
		String directorio = "C:/.../";	// Dir donde guardo archivos
		try {

		    con = AccesoDatos.getConexion();

		    String orden_SQL = "SELECT nombre, apellido1, apellido2, nif, nacimiento, email, dedicacion, " +
		    					"curriculum, observaciones FROM investigador WHERE nif = '" + strNIF +
		    					"' ORDER BY apellido1, apellido2,nombre";
		    Statement sentencia = con.createStatement();
		    ResultSet rs = sentencia.executeQuery( orden_SQL );

		    rs.next();

		    //// Si se ha encontrado el registro
		    if ( rs.getRow() != 0) {

		    	//// Streams de salida
		    	FileOutputStream streamCv = new FileOutputStream( directorio + strArchivoCv );
		    	FileOutputStream streamObs = new FileOutputStream( directorio + strArchivoObs );

		    	//// Escribo en streams y cierro
		    	streamCv.write( rs.getBytes("curriculum") );
		    	streamCv.close();
		    	streamObs.write( rs.getBytes("observaciones") );
		    	streamObs.close();

		    	//// Compruebo que existen los archivos
		    	File fObs = new File(directorio +strArchivoObs);
		    	File fCv = new File(directorio +strArchivoCv);
		    	if ( !fObs.exists() || !fCv.exists())
		    		resultado = 0;
		    }
		    else
		    	resultado = -1;		// No se ha encontrado registro

		    sentencia.close();
		}
		catch (Exception e) {
			resultado = 0;
		}
		AccesoDatos.cerrarConexion(con);
		return resultado;
  }


Update a partir de un archivo

En el siguiente ejemplo se actualiza el campo CLOB de observaciones a partir de un archivo. El nombre del archivo lo pasamos como segundo argumento. El primer argumento es el NIF de la persona. Este método devuelve 1 si se ha producido la actualización, 0 en caso contrario.

Usamos el método canRead() de File para comprobar que el archivo se puede abrir. A continuación creamos un stream de entrada a partir del archivo. Lo esencial viene inmediatamente después, por medio de setBinaryStream() de la clase PreparedStatement indicamos que volcamos el stream sobre la columna de observaciones. Por último, ejecutamos la sentencia preparada (PreparedStatement).


   /************************************************************************************
	Actualiza campo de observaciones de la fila cuyo nif sea 'strNIF'. La actualización
	se realiza a partir de un archivo (strArchivo)
   **************************************************************************************/
   public static int updateObservaciones( String strNIF, String strArchivo ) {
		Connection con = null;
		int resultado = 0;
		try {
			con = AccesoDatos.getConexion();

			File archivo = new File( strArchivo );
			if ( !archivo.canRead() )
				return 0;

			FileInputStream streamEntrada = new FileInputStream( archivo );
			PreparedStatement pstmt = con.prepareStatement("update investigador set observaciones = ? where nif='" + strNIF+"'");
			pstmt.setBinaryStream(1, streamEntrada, (int)archivo.length());
			resultado = pstmt.executeUpdate();
			pstmt.close();
			streamEntrada.close();

		}
		catch (Exception e) {
			e.getMessage();
		}
		AccesoDatos.cerrarConexion(con);
		return resultado;
   }

Para hacer update desde archivo con el campo BLOB 'curriculum' el proceso sería el mismo.


Volver al índice