Mapear BFILES con Hibernate

En esta wiki se va a describir cómo mapear tipos de datos BFILE de Oracle para trabajar directamente con ficheros en Hibernate/JPA.

Un BFILE no es un fichero, es un puntero a un fichero que se encuentra en un directorio gestionado por Oracle.

Ficheros que intervienen en el mapeo:

  • FundeWebOracle10gDialect: Dialect para registrar los types de Oracle. Librería fundeweb-hibernate a partir de las versiones: [1.5.107 , 2.0.13, 2.0.111].
  • BFileToByteArrayType: type para trabajar directamente con byte[] en la entidad.
  • BFileType: type para trabajar con OracleBfile en la entidad.
  • PreventRemove: Listener básico para prevenir que no se borren los registros de la entidad (si se borra un bfile/puntero puede dejar un fichero huérfano en los directorios de la bbdd). Librería fundeweb-jpa a partir de las versiones: [1.5.113 , 2.0.18, 2.0.103].

De momento esta configuración solo es válida para la LECTURA de BFILEs.


1. Fichero persistence.xml


Sustituir la propiedad hibernate.dialect por:

<property name="hibernate.dialect" value="es.um.atica.hibernate.dialect.FundeWebOracle10gDialect"/>


2. Entidad

- Anotar la entidad que contiene la columna BFILE con:

 @EntityListeners( PreventRemove.class )
 


Elegir una de las siguientes opciones para mapear el BFILE en la entidad.

- [OPCIÓN A] Si el formato de los ficheros a recuperar es conocido y siempre es el mismo:

  • Declarar la columna del BFILE como byte[]
private byte[] fichero;
  • Anotar el get de la columna con:
  @Column( name = "[NOMBRE_COLUMNA]", updatable = false, insertable = false )
  @Type( type = "es.um.atica.hibernate.type.BFileToByteArrayType" )
  public byte[] getFichero() {
	return fichero;
  }


- [OPCIÓN B] Si el formato de los ficheros a recuperar no es conocido o puede variar:

  • Declarar la columna como oracle.jdbc.OracleBfile
private OracleBfile fichero;
  • Anotar el get de la columna con:
  @Column( name = "[NOMBRE_COLUMNA]", updatable = false, insertable = false )
  @Type( type = "es.um.atica.hibernate.type.BFileType" )
  public OracleBfile getFichero() {
	return fichero;
  }
 
  • Crear un método Transient para obtener el byte[]:
       //Ejemplo con columna 'fichero'
	@Transient
	public byte[] getFicheroBytes() throws SQLException {
 
		byte[] result = null;
 
		try {
			if ( this.fichero != null && this.fichero.fileExists() ) {
				this.fichero.openFile();
				java.io.ByteArrayOutputStream bao = new java.io.ByteArrayOutputStream();
				java.io.InputStream in = this.fichero.getBinaryStream();
				byte[] buffer = new byte[1024];
				int len = -1;
				while ( ( len = in.read( buffer, 0, buffer.length ) ) > -1 ) {
					bao.write( buffer, 0, len );
					bao.flush();
				}
				bao.close();
				result = bao.toByteArray();
				this.fichero.closeFile();
			}
		} catch ( Exception ex ) {
			throw new SQLException( "Could not read OracleBfile:", ex );
		}
 
		return result;
	}
  • Crear un método Transient para obtener el nombre del fichero:
        //Ejemplo con columna 'fichero'
        @Transient
	public String getFicheroName() throws SQLException {
 
		String result = null;
 
		if ( this.fichero != null && this.fichero.fileExists() ) {
			result = this.fichero.getName();
		}
 
		return result;
	}


3. Manejador

Ejemplo con columna byte[] en FundeWeb 2.0 (OPCIÓN A)

//ejemplo simplificado con entidad 'f' y columna 'fichero' 
InputStream stream = new ByteArrayInputStream( f.getFichero() ); 
StreamedContent fileDownload = new DefaultStreamedContent( stream, "application/pdf", "miFichero.pdf");


Ejemplo con columna OracleBfile en FundeWeb 2.0 (OPCIÓN B)

//ejemplo simplificado con entidad 'f' y columna 'fichero'
InputStream stream = new ByteArrayInputStream( f.getFicheroBytes() ); 
String mimeType = java.net.URLConnection.guessContentTypeFromName( f.getFicheroName() );
 
StreamedContent fileDownload = new DefaultStreamedContent( stream, mimeType, f.getFicheroName() );


Como se puede observar, la configuración de la OPCIÓN A es más simple.
Como contrapartida, tendremos que gestionar desde el manejador el nombre y el mimetype del fichero a descargar.
Esta configuración es OPCIONAL cuando el tipo de archivo apuntado es fijo y conocido.

La configuración de la OPCIÓN B, no es tan directa como la anterior, pero permite recuperar desde base de datos tanto el nombre como la extensión del fichero apuntado por el BFILE.
Esta configuración es OBLIGATORIA cuando el tipo de archivo apuntado puede variar.
Esta configuración es OPCIONAL cuando el tipo de archivo apuntado es fijo y conocido.

Se deja a criterio del programador usar una opción de mapeo u otra.

4. Permisos a los directorios

Para poder acceder a los ficheros desde los diferentes entornos de nuestra aplicación habrá que solicitar, via JIRA a SISTEMAS (proyecto BD - DJ-AT-SIST-BD),
permisos de lectura para el usuario JV_ de nuestra aplicación a los directorios apuntados por los BFILEs.

Pueden encontrarse los directorios apuntados haciendo una consulta a la tabla que contiene el BFILE.
El BFILE aparecerá con formato:

bfilename('DIRECTORIO','NOMBRE_FICHERO')

.
Ante cualquier duda al respecto, poneos en contacto con MNCS.

  • fdw2.0/fundeweb2.0/gt/jpa/mapeo_bfiles_jpa.txt
  • Última modificación: 12/05/2021 14:02
  • por RAMON GINEL GEA