El objetivo primordial de este artículo es describir una herramienta que puede resultar bastante útil a la hora de algo tan común como es realizar un informe. Vamos a entrar en aspectos básicos de cara a su rápida utilización y aplicación.
¿Qué es Dynamic Jasper? Dynamic Jasper es una librería que corre sobre JasperReports y que nos simplifica enormemente la realización de informes. Las posibilidades son enormes (posibilidad de agregar Charts, encadenar reportes, añadir códigos de barras a las columnas de un informe, …) además de usos totalmente básicos. En este artículo nos centraremos en conceptos introductorias que podemos ir combinando para obtener resultados cada vez mejores y más elegantes a la vez de poder sumar aspectos más complejos.
Su funcionamiento básico de DJ se centra en hacer más amigable e intuitivo el uso de JasperReports que para nosotros será una caja negra. Es decir, DJ actuará como un disfraz, aunque realmente seguiremos usando JasperReports, pero ahorrándonos todos los engorrosos pasos que supone usarlo.
La web del proyecto contiene un amplio abanico de documentación y ejemplos que nos pueden ser útiles de cara a realizar elegantes informes.
Entorno de integración Dynamic Jasper es una librería escrita 100% en Java con lo que es fácilmente integrable en proyectos que empleen el lenguaje Java.
No es necesaria ninguna herramienta adicional o “plug-in” al margen de nuestro entorno de desarrollo (IDE) preferido.
Herramientas necesarias o Dependencias
En la página web de la librería podemos observar las dependencias. Aquí indicaremos que librerías vamos a necesitar integrar en nuestros proyectos para poder usar DJ en nuestro proyecto
- La librería propia de DJ
- Las librerías que componen Jasper Reports teniendo en cuenta que desde DJ 3.0.5 se necesita Jasper Reports 3.5.
- Dependencias: Commons BeanUtils, Commons Collections, Commons Logging, Compilador de Eclipse jdt-core.
- Para exportar a PDF necesitamos la librería iText
- Para exportar a Excel necesitamos la librería JExcel API y el conjunto de librerías POI de Apache. Esta última librería no está soportada en su versión beta 3.5, es decir, para que no existan problemas necesitamos la versión 3.2
- Para exportar a XML necesitamos la librería Commons Digester
- Luego librerías propias de generación de Charts (JFreeChart , JCommon) o de generación de Barcotes o códigos de barra (barbecue 1.1)
Nuestro primer reporte
Una vez sabemos que tenemos que tener montado en nuestro espacio de trabajo sólo nos queda empezar a desarrollar para empezar a ver los primeros resultados. Vamos a hacer un primer informe para observar los primeros resultados.
Definir esqueleto
La idea de realizar un informe es partir de una especie de esqueleto que iremos rellenando añadiendo cosas.
Este esqueleto se puede definir de varias formas como pueden ser las siguientes:
FastReportBuilder drb = new FastReportBuilder();
DynamicReportBuilder drb = new DynamicReportBuilder();
De este modo ya tenemos el esqueleto en la variable ‘drb’. Ahora procederemos a añadirle cosas. Veremos que las posibilidades que hay y la cantidad de añadidos son bastante interesantes.
Hagámonos a la idea de que si generáramos el informe de estos esqueletos obtendríamos un informe en blanco, sin nada que mostrar. Eso es lo que vamos a ir haciendo ahora, añadir datos.
Añadir Primeros Detalles
DJ nos da la posibilidad de añadir detalles que nos resultarán útiles de cara a facilitar determinada información o añadir aspectos elegantes al informe.
Podemos añadir un título a informe
drb.setTitle("Equipos");
Definición de márgenes en la página
drb.setLeftMargin(margen).setRightMargin(margen).setTopMargin(margen).setBottomMargin(margen);
Definimos la anchura de cabecera del informe y del detalle del informe.
drb.setDetailHeight(anchura).setHeaderHeight(anchura);
Podemos hacer que la página se divida en varias columnas y ahí insertar los datos
//Si optamos por más de una columna podemos definir la separación entre ellas
drb.setColumnsPerPage(columnasPorPagina, separacion);
//Definimos una columna por página
drb.setColumnsPerPage(1);
Podemos añadir autotextos
//Insertamos una Fecha en la cabecera a la izquierda
drb.addAutoText(sdf.format(new Date()), AutoText.POSITION_HEADER, AutoText.ALIGMENT_LEFT);
//Insertamos “Pagina X/Y” en la cabecera a la derecha
drb.addAutoText(AutoText.AUTOTEXT_PAGE_X_SLASH_Y, AutoText.POSITION_HEADER, AutoText.ALIGMENT_RIGHT);
Como vemos podemos usar más autotextos cuyo tipo será definido en el primer parámetro:
- AutoText.AUTOTEXT_CREATED_ON : Nos indicará la fecha de creación del reporte
- AutoText. AUTOTEXT_PAGE_X_SLASH_Y: Página X/Y
- “Texto cualquiera”
Además de colocarlo donde queramos en la página.
Como hemos comentado con anterioridad, en la página de la librería podemos obtener muchísima más información acerca añadir bastantes más cosas al reporte.
Añadir Columnas
Vamos a comenzar a definir los datos que queremos mostrar en nuestro informe. Supongamos en nuestro ejemplo que tenemos una Collection de objetos de la siguiente clase:
public class Equipo {
private int id;
private String nombre;
private String fundacion;
private String presidente;
private String entrenador;
private LinkedList<Jugador> jugadores;
private String rutaEscudo;
private InputStream escudo;
public Equipo() {
}
public Equipo(int id, String nombre, String fundacion, String presidente, String entrenador, LinkedList<Jugador> jugadores) {
this.id = id;
this.nombre = nombre;
this.fundacion = fundacion;
this.presidente = presidente;
this.entrenador = entrenador;
this.jugadores = jugadores;
}
public InputStream getEscudo() {
return escudo;
}
public String getRutaEscudo() {
return rutaEscudo;
}
public void setRutaEscudo(String rutaEscudo) {
this.rutaEscudo = rutaEscudo;
try {
File fileIn = new File(rutaEscudo);
this.escudo = new FileInputStream(fileIn);
} catch (Exception e){
e.printStackTrace();
}
}
/*resto de Getters and Setters */
…
Queremos un informe que muestre el listado de datos de esa Collection.
Vamos a definir un informe donde podamos ver los datos en columnas, por tanto, tenemos que definir esas columnas. Veamos como esto nos va a resultar bastante sencillo:
AbstractColumn c1 = ColumnBuilder.getInstance().setColumnProperty("id", Integer.class.getName()).setTitle("ID").setWidth(10).build();
AbstractColumn c2 = ColumnBuilder.getInstance().setColumnProperty("nombre", String.class.getName()).setTitle("Nombre").setWidth(30).build();
AbstractColumn c3 = ColumnBuilder.getInstance().setColumnProperty("fundacion", String.class.getName()).setTitle("Fundacion").setWidth(30).build();
AbstractColumn c4 = ColumnBuilder.getInstance().setColumnProperty("presidente", String.class.getName()).setTitle("Presidente").setWidth(30).build();
//Añadimos las columnas al informe
drb.addColumn(c1);
drb.addColumn(c2);
drb.addColumn(c3);
drb.addColumn(c4);
Para crear las columnas indicamos nombre del atributo, cadena que representa la clase del atributo (class.getName()), el título que queremos mostrar en el informe y la anchura (medida proporcional) de la columna. Existen muchas más opciones que podemos añadir a la columna a la hora de crearla.
También podemos crear las columnas de la siguiente manera
drb.addColumn("TITULO", "id", Integer.class.getName(),10);
Una vez hemos realizado esto tenemos el informe disponible para ser visualizado. Debemos realizar una serie de operaciones antes que veremos en el siguiente apartado.
Generación del Reporte
Una vez hemos formado el informe debemos realizar unas últimas comprobaciones de cara a poder visualizarlo. A parte de estas comprobaciones deberemos indicarle de donde va a sacar los datos para mostrar, es decir, también deberemos indicarle cual es el DataSource origen. Adicionalmente podemos indicar algunas opciones finales antes de realizar estos pasos, por ejemplo:
drb.setUseFullPageWidth(true);
Indicamos que queremos usar todo el ancho de la página a la hora de mostrar el informe.
Ahora realizamos la compilación del esqueleto rellenado para comprobar si hay algún problema y no se puede continuar con el mostrado del informe.
DynamicReport dr = drb.build();
Una vez que se ha realizado el compilado y todo es correcto procedemos a añadirle la Collection que contiene los datos
//Definimos la fuente de datos
JRDataSource ds = new JRBeanCollectionDataSource(l);
//Generamos el informe como JasperPrint con los datos ya añadidos.
JasperPrint jp = DynamicJasperHelper.generateJasperPrint(dr, new ClassicLayoutManager(), ds);
//Podemos también añadir un mapa de parámetros que hayamos definido anteriormente
//JasperPrint jp = DynamicJasperHelper.generateJasperPrint(dr, new ClassicLayoutManager(), ds, map);
Una vez todos estos procesos se han realizado correctamente con el objeto JasperPrint podemos hacer multitud de operaciones: podemos mostrarlo usando la herramienta JasperViewer, podemos exportarlo a otros formatos (apartado 6), ...
JasperViewer es un visor que proporciona la librería que muestra el informe además de permitirnos la exportación de los datos a través de él a PDF, XML, ODT, ...
//Mostramos el reporte mediante el visor JasperViewer. Con el false, indicamos que no
//cierre la aplicación al cerrar el JasperViewer
JasperViewer.viewReport(jp, false);
El resultado debería ser algo parecido a esto:
Reportes con Plantillas
Ya hemos visto que realizar un reporte simple a fin de cuentas no es más que crear un objeto DynamicReportBuilder o FastReportBuilder, añadirle columnas, compilarlo, asociarle un DataSource y mostrarlo. Ahora bien, pongámonos en el caso de que queremos seguir un formato específico de informe o plantilla. Por ejemplo, una ficha de empleado o, como en el caso de ejemplo, una especie de ficha con los datos de los equipos (los que hemos visto en el apartado anterior). Veremos que con DJ es bastante fácil de realizar.
Definir una plantilla
Para definir o crear una plantilla debemos usar alguna herramienta que nos permita la edición de las mismas y la creación de archivos JRXML. Un programa que realiza este tipo de plantillas es el iReport, bastante intuitivo y fácil de manejar. No vamos a entrar en profundidad en el manejo de esta herramienta, sólo describiremos la plantilla que hemos realizado para realizar este elemento.
- Podemos observar que hemos colocado siete elementos en la banda de detalle ('detail'): tres etiquetas o 'label' que son elementos estáticos del informe, tres campos TextField que contendrán tres Fields y un campo Image.
- Definiciones de Fields de la plantilla. Vemos como hemos definido tres Field de tipo String (nombre, fundación y presidente) y un field de tipo InputStream (escudo). Veremos en el siguiente apartado para que sirven estos Fields.
Nota: En versiones nuevas de iReport se generan las plantillas con un atributo en las bandas que pueden dar problemas. Al seleccionar una banda vemos que posee un atributo llamado 'Split Type' que por defecto tiene el valor 'Strecht'. Para solucionar el problema podemos poner en ese atributo el valor '<Default>'.
Agregar campos y asociar plantilla
Una vez hemos creado la plantilla debemos guardarla con formato JRXML. Lo ideal sería guardarla dentro del proyecto ya que para futuras modificaciones o traslados del proyecto no se nos olvide mover dicha plantilla.
Una vez guardada la plantilla debemos asociarla a nuestro informe para indicarle que debe ajustarse a ella a la hora de mostrar los datos. Asignar una plantilla a un informe es tan fácil como poner la siguiente instrucción:
//Asociamos una plantilla al informe
drb.setTemplateFile("resources/equipos.jrxml");
Ya que le hemos asociado una plantilla debemos crear una 'conexión' entre los datos que tenemos en la aplicación y la plantilla. Como hemos visto anteriormente habíamos creado unos Fields en la plantilla. Ahora crearemos unos campos (Fields) en el informe. Esos campos serán el nombre de los atributos y definiremos su tipo. Vemos como lo hemos realizado en el código.
drb.addField("escudo", InputStream.class.getName());
drb.addField("nombre", String.class.getName());
drb.addField("presidente", String.class.getName());
drb.addField("fundacion", String.class.getName());
A modo de resumen podemos decir que en la plantilla hemos representando gráficamente como mostrar el objeto indicando donde aparecen los atributos y definiendo éstos en la misma. Del mismo modo asociamos esa plantilla al informe y en éste definimos los atributos del objeto.
Generación del Reporte
Una vez hemos realizado estos pasos, sólo nos queda generar el informe. Esta generación es la misma que seguimos en el informe del listado, no existe diferencia alguna. Una vez se ha lanzado el JasperViewer veremos que obtenemos el siguiente informe.
Subreportes
Realizar subreportes con Jasper Reports es bastante confuso y enrevesado. Sin embargo, vamos a ver como realizar subinformes usando la librería DJ es mucho más simple y útil.
Para empezar, vamos a definir la estrategia para realizar este informe.
Paso 1 : Definición Informe
Nuestro objetivo final es definir un informe como subinforme de otro, por tanto, para empezar definiremos dos informes inicialmente independientes. Vamos a crear un informe de tipo plantilla que va a contener un informe de tipo listado (la plantilla contendrá los datos de los equipos y el listado mostrará los jugadores del equipo)
Por tanto, procedemos a definir ambos informes.
- Informe de Plantilla (Equipo)
public static FastReportBuilder initMainReport() throws Exception {
FastReportBuilder report = new FastReportBuilder();
report.setTitle("EQUIPOS");
report.setMargins(margen, margen, margen, margen);
report.setTemplateFile("resources/equipos.jrxml");
report.addField("id", Integer.class.getName());
AbstractColumn c1 = ColumnBuilder.getInstance().setColumnProperty("id", Integer.class.getName()).setTitle("ID").setWidth(10).build();
report.addColumn(c1);
report.addField("nombre", String.class.getName());
report.addField("fundacion", String.class.getName());
report.addField("presidente", String.class.getName());
report.addField("entrenador", String.class.getName());
report.addField("escudo", InputStream.class.getName());
return report;
}
Importante: Podemos observar como hemos definido una columna en el informe a pesar de ser de tipo plantilla. En el siguiente apartado explicaremos la razón.
public static FastReportBuilder initSubreport() throws Exception {
FastReportBuilder report = new FastReportBuilder();
AbstractColumn c1 = ColumnBuilder.getInstance().setColumnProperty("id", Integer.class.getName()).setTitle("ID").setWidth(10).build();
AbstractColumn c2 = ColumnBuilder.getInstance().setColumnProperty("dorsal", Integer.class.getName()).setTitle("Dorsal").setWidth(30).build();
AbstractColumn c3 = ColumnBuilder.getInstance().setColumnProperty("nombre", String.class.getName()).setTitle("Nombre").setWidth(30).build();
AbstractColumn c4 = ColumnBuilder.getInstance().setColumnProperty("posicion", String.class.getName()).setTitle("Posicion").setWidth(30).build();
report.addColumn(c1);
report.addColumn(c2);
report.addColumn(c3);
report.addColumn(c4);
report.setUseFullPageWidth(true);
return report;
}
Como vemos, nada extraño respecto a lo que explicamos en el apartado de informes de tipo listado
Paso 2 : Agrupación
Definimos sobre que campo queremos agrupar el subinforme.
Bien, hemos visto que se ha creado una columna en el informe de plantilla que realmente no es necesaria pues hemos añadido un Field con ese atributo pero, ¿por qué hacemos esto?.
A la hora de colocar el subinforme debemos indicar un campo significativo para colocar el subinforme, es decir, necesitamos un identificador del objeto para añadir el subinforme. Veamoslo en el ejemplo: Tenemos equipos que poseen un entero llamado 'id' que es el identificador. A la hora de crear el informe para cada valor de ese 'id' se añadirá un subinforme con los datos correspondientes al objeto de ese id.
La agrupación de puede realizar creando un grupo o como veremos en el siguiente apartado
Paso 3 : Asociación
Asociamos un informe como subinforme del otro. Veamos como lo hacemos:
private static DynamicReport definirSubreport(FastReportBuilder report, FastReportBuilder subreport, String atributoColeccion, String claseColeccion) throws Exception {
report.addField(atributoColeccion, claseColeccion);
report.addGroups(1).setGroupLayout(1, GroupLayout.EMPTY);
report.addSubreportInGroupFooter(1, subreport.build(),
new ClassicLayoutManager(), atributoColeccion,
DJConstants.DATA_SOURCE_ORIGIN_FIELD,
DJConstants.DATA_SOURCE_TYPE_COLLECTION);
report.setUseFullPageWidth(true);
return report.build();
}
Para empezar definimos un Campo en el informe 'padre' que no es necesario que sea definido en la plantilla. Ese campo contiene el nombre del atributo del objeto (equipo en nuestro caso) de la colección que mostraremos en el subinforme. Esa colección actuará de DataSource del subinforme.
Definimos el grupo, con la función 'addGroups(1)' donde el parámetro indica el número de columnas que cogeremos para agrupar. En nuestro caso, la primera y única columna definida para el informe con plantilla.
Una vez realizado esto, usamos la función 'addSubreportInGroup(Header|Footer)' para asociar ambos informes. Los parámetros que se pueden observar son: el primero (número 1) es el número del grupo que usamos para agrupar, en este caso el primero; el subreport compilado, es decir, DynamicReport del subinforme; un Layout, nosotro usaremos el clásico; el nombre del atributo de la clase que alimenta a modo de DataSource al subinforme; constante que indica que el DataSource proviene de un campo/field del informe y constante que indica que el tipo del DataSource es una colección.
Una vez realizado esto compilamos el informe y realizamos la generación del informe y obtendremos los siguiente:
Formas de Mostrar un reporte
La herramienta JasperViewer es bastante útil de cara a realizar una especie de previsualizado y desde ella realizar una exportación. Muchas veces nos interesa exportar directamente informes en nuestro máquina o en la máquina del cliente, por ejemplo, si generamos informes masivamente y queremos exportarlos a varios ficheros ya que sería bastante engorroso que JasperViewer nos mostrara todos los informes y nosotros los exportaramos.
Exportación PDF, HTML, XML
DJ proporciona métodos para exportar nuestros informes a formatos conocidos y comunes. Tenemos la posibilidad de exportar a PDF, HTML y XML, entre otros.
La ventaja que proporciona DJ es que exportar a cualquiera de estos formatos nos cuesta una sóla línea. Simplemente, en lugar de notificar a JasperViewer lo que queremos mostrar usaremos una clase llamada 'JasperExportManager' que nos proporcionará soluciones de cara a las exportaciones. Vemos que métodos podemos usar:
//Exportación a formato HTML
JasperExportManager.exportReportToHtmlFile(jp, EXPORT_PATH + "exportacionHTML.html");
//Exportación a formato PDF
JasperExportManager.exportReportToPdfFile(jp, EXPORT_PATH + "exportacionPDF.pdf");
//Exportación a formato XML, sin embeber imágenes
JasperExportManager.exportReportToXmlFile(jp, EXPORT_PATH + "exportacionXML.1.xml", false);
//Exportación a formato XML, embebiendo imágenes
JasperExportManager.exportReportToXmlFile(jp, EXPORT_PATH + "exportacionXML.2.xml", true);
Como vemos la exportación es bastante simple, una sóla línea nos proporciona un fichero o la información del informe.
Exportación a Excel
La exportación a Excel es ligeramente más compleja que las que hemos visto anteriormente ya que realmente no existe una mecanización por parte de DJ de exportación a este tipo de archivo. La documentación de DJ si que nos da información acerca de como exportar un informe a XLS.
Vamos a incluir un ejemplo de método para exportar un objeto JasperPrint (el jp que hemos mostrado anteriormente) a un archivo Excel.
private static void exportToExcel(JasperPrint jp) throws Exception {
//Creamos una instancia del objeto JRXlsExplorer que se encuentra en la librería jasperreports-X.X.X.jar
JRXlsExporter exporter = new JRXlsExporter();
//Si queremos definir varios Sheet lo hacemos en un array de cadenas que posteriormente la añadiremos por parámetros.
String[] sheetNames = {"Hoja 1"};
//Creamos el fichero de salida donde exportaremos el informe a Excel
File outputFile = new File(EXPORT_PATH + "exportacionEXCEL.xls");
FileOutputStream fos = new FileOutputStream(outputFile);
//Empezamos a definir los parámetros de la exportacion
//Indicamos el objeto JasperPrint que deseamos exportar
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp);
//Indicamos el fichero donde vamos a exportar el informe
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, fos); //and output stream
//Excel specific parameter
//Indicamos si queremos una página por Sheet
exporter.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
//Indicamos si deseamos eliminar los espacios vacíos entre filas
exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
//Indicamos si quremos mostrar una página en blanco como fondo
exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
//Definimos los nombres de los Sheet que es el Array de String que definimos arriba
exporter.setParameter(JRXlsExporterParameter.SHEET_NAMES, sheetNames );
//Usamos la función exportReport para crear el nuevo Excel.
exporter.exportReport();
}
Todos estos atributos los podemos ver en la documentación de Jasper Reports, o podemos verlo en el Javadoc que podemos encontrar en la red.
Nota: Indicar que para que podamos realizar la exportación a EXCEL las últimas versiones de DJ no soportan la exportación con las librerías del POI de Apache 3.5, ya que según el equipo de desarrollo de DJ hasta que no dejara de estar POI 3.5 en fase beta no se adaptaría DJ a dicha librería.
Enlaces