Вы находитесь на странице: 1из 28

lunes 2 de marzo de 2009

Creacin de Reportes con JasperRepots y iReports - Parte 2: Uso de DataSources Personalizados


En el primer post de la serie de JasperReports habl de cmo crear una biblioteca para trabajar con JasperReports y cmo crear un reporte en mltiples formatos. Adems hicimos un ejemplo de creacin de reportes usando una conexin JDBC directa a nuestra base de datos. Ahora veremos cmo generar nuestros reportes sin hacer uso de una conexin. Esto puede ser muy til en los casos en los que ya tengamos los datos en memoria y no necesitemos realizar una conexin a la base de datos; o en el caso en que, por alguna razn ajena a nosotros, no podamos obtener la conexin. Para poder hacer esto se nos proporciona una interface: "net.sf.jasperreports.engine.JRDataSource". Esta interface tiene solo dos mtodos:

getFieldValue(JRField jrField) next()

Explicar estos mtodos un poco ms adelante, por ahora comencemos a generar todo lo necesario para tener nuestro reporte. Creamos un nuevo proyecto en NetBeans (men "File -> New Project..."). En la ventana de nuevos proyectos seleccionamos la categora "Java" y como tipo de proyecto seleccionamos "Java Application" y hacemos clic en el botn "Next". En la siguiente ventana introducimos el nombre de nuestro proyecto y dejamos seleccionada las opciones "Create Main Class" y "Set as Main Project". Para que podamos pasar datos a nuestro reporte es necesario que utilicemos un datasource. En el ejemplo anterior veamos que el

datasource que usbamos era la base de datos y la conexin a la misma. En esta ocasin el datasource ser una de nuestras clases. Esta clase deber implementar la interface "JRDataSource" y contendr la lgica para pasar los datos correspondientes a nuestro reporte. Creamos una nueva clase haciendo clic derecho sobre el nodo " Source Packages" de la ventana "Projects", o sobre el paquete que ya tenemos creado, y en el men contextual que se abre seleccionamos la opcin "New -> Java Class...".

En la ventana que se abre escribimos como en nombre de la nueva clase "Participante" y presionamos el botn "Finish". Con esto veremos en la ventana de nuestro editor la nueva clase "Participante". Esta clase "Partcipante" representar los datos que se mostrarn en el reporte, tal como en el ejemplo anterior cada uno de los registros de la tabla "participantes" representaba una fila en el reporte final, cada objeto participante que creemos representar una fila en el nuevo reporte. Agreguemos los siguientes atributos a nuestra clase:

private int id; private String nombre;

private String username; private String password; private String comentarios;

Adems agregamos sus getters y sus setters y dos constructores, uno sin argumentos y uno que reciba como argumentos todas las propiedades declaradas anteriormente. Si estn utilizando el NetBeans para seguir este tutorial pueden generar tanto los setters y los getters de forma automtica presionando las teclas "Alt + Insert", con lo cual se abrir un men contextual con las opciones necesarias para hacer esto:

Al terminar, su clase debe verse ms o menos as:

public class Participante { private int id; private String nombre; private String username; private String password; private String comentarios;

public Participante() { }

public Participante(int id, String nombre, String username, String password, String comentarios) { this.id = id; this.nombre = nombre; this.username = username; this.password = password; this.comentarios = comentarios; }

public String getComentarios() { return comentarios; }

public void setComentarios(String comentarios) { this.comentarios = comentarios; }

public int getId() { return id; }

public void setId(int id) {

this.id = id; }

public String getNombre() { return nombre; }

public void setNombre(String nombre) { this.nombre = nombre; }

public String getPassword() { return password; }

public void setPassword(String password) { this.password = password; }

public String getUsername() { return username; }

public void setUsername(String username) { this.username = username; }

Ahora, antes de seguir con nuestro cdigo Java, pasaremos a crear la plantilla de nuestro reporte con iReport, por lo que abrimos esta herramienta. En esta ocasin no haremos uso de un wizard para crear el reporte, sino que lo haremos desde cero, y aprovechar para explicar algunos conceptos bsicos sobre los reportes conforme los vayamos necesitando. Para crear el nuevo reporte vamos el men "File -> New...". En la ventana que se abre seleccionamos uno de los formatos "Blank" y presionamos el botn "Open this Template":

Despus introducimos el nombre del reporte, yo lo llamar "reporte2.jrxml", y la ubicacin en la que se guardar. Les recomiendo que en esta ocasin guarden el reporte en el directorio raz del proyecto de NetBeans que acabamos de crear. Hacemos clic en el botn "Next" y se nos mostrar un mensaje de felicitacin por haber creado un nuevo reporte (^_^!). Cuando presionen el botn "Finish" vern una hoja dividida en 7 u 8 diferentes porciones horizontales llamadas bandas.

Cuando generamos nuestro reporte final con datos, cada una de estas bandas se comporta de una forma distinta. Algunas aparecen solo al principio o al final del reporte, otras aparecen con cada nueva fila que se le agrega, otras solo al final de los datos, etc. La plantilla de reportes est dividida en 10 bandas predefinidas. Una banda siempre tiene el mismo ancho que la pgina. Sin embargo el alto de algunas de las bandas puede variar durante la fase de llenado de datos, an si establecimos un alto en el momento del diseo. Las bandas existentes son:

Background Title Page Header Column Header Detail 1 Column Footer Page Footer Last Page Footer Summary

No Data

Explicar algunas de estas bandas conforme las vayamos usando en los tutoriales correspondientes. Lo primero que haremos es agregar texto a nuestro reporte. En iReport existen dos tipos de texto: texto esttico y texto dinmico (tambin llamado expresiones). El texto esttico es aquel que no cambia, mientras que las expresiones son como etiquetas que le indican a JasperReports que debe reemplazarlas por algn valor al momento de generar el reporte final.

Existen 3 tipos de expresiones:


Campos (fields) representados con "$F{nombre_campo}". Variables representadas por "$V{nombre_variable}". Parmetros representados por "$P{nombre_parmetro}".

Cada uno de estos tipos de expresiones tiene un uso particular: Los campos ("$F{}") le dicen al reporte dnde colocar los valores obtenidos a travs del datasource. Por ejemplo, nuestro objeto "Partcipante" tiene un atributo llamado "username". Usando una expresin de campo indicamos en cul parte o seccin del reporte debe aparecer el valor de ese atributo usando "$F{username}". Esto quedar ms claro un poco ms adelante.

Los parmetros ("$P{}") son valores que usualmente se pasan al reporte directamente desde el programa que crea el JasperPrint del reporte (en nuestra aplicacin Java). Aunque tambin existen algunos parmetros internos que podemos leer pero no modificar. Para hacer uso de estos parmetros simplemente indicamos el nombre del parmetro en el lugar que queremos colocarlo. Pueden encontrar los nombres y significados de los parmetros internos en la documentacin de JasperReports.

Las variables ("$V{}") son objetos usados para almacenar valores como los resultados de clculos. Al igual que con los parmetros, JasperReports tiene algunas variables internas que podemos leer. Pueden encontrar los nombres y significados de las variables la documentacin de JasperReports. Cada uno de estos elementos tiene un nombre, un tipo (que debe corresponder con un tipo de objeto Java como String o Integer), y una descripcin opcional. Adems deben ser registrados para poder ser usados en tiempo de diseo y que puedan ser entendidos al momento de compilar el reporte, y por lo tanto para que nuestro reporte funcione correctamente en tiempo de ejecucin. Esto lo veremos un poco ms adelante. Regresemos a donde nos quedamos. Queremos agregar un texto esttico a nuestro reporte a modo de ttulo. Para esto debemos agregar un elemento llamado "Static Text". Los elementos que podemos agregar a nuestros reportes estn en una ventana llamada "Palette", que se encuentra en la parte derecha de iReport, bajo la categora "Report Elements". Si no pueden ver la ventana "Palette" presionen las teclas "Ctrl + Shift + 8":

De esta ventana arrastramos el elemento "Static Text" a la banda "Title". "Title" es una banda que solo aparece en la parte superior de

la primer pgina. Por lo que nos sirve para colocar el ttulo del reporte y/o el nombre y logo de nuestra empresa. Una vez que hayamos colocado el texto esttico en la banda correspondiente y mientras an est seleccionado, modificamos sus propiedades usando la ventana de propiedades del elemento y la barra de formato de texto. Tambin podemos abrir otra ventana que nos ayude a alinear y controlar el alto y el ancho de los elementos yendo al men "Window -> Formatting Tools".

Ahora agregamos los encabezados de las columnas en las que se mostrarn los datos que pasaremos al reporte. Agregamos estos nombres en la banda "Column Header", la cual se repetir en cada pgina antes de mostrar los datos de las columnas.

Agregaremos las columnas "Nombre", "Usuario", "Contrasea", y "Comentarios". Por lo que nuevamente arrastramos un elemento "Static Text" para cada una de las columnas. Tambin podemos ajustar el alto de esta banda para que se ajuste al de nuestro texto, ya sea movindola directamente en el diseador, o desde la ventana de propiedades:

Nuestro reporte ya casi est terminado, ahora solo nos queda agregar los campos en los que se mostrarn los datos que pasaremos al reporte en un momento. Para que estos campos puedan ser reemplazados por el valor real es necesario que usemos las expresiones (texto dinmico) de las que habl anteriormente. Para esto definiremos un "field" para cada uno de los campos que queramos mostrar (en este caso sern los mismos campos para los que definimos las cabeceras de las columnas). Los fields deben definirse antes de poder ser usados. Esta definicin incluye el nombre del field y su tipo. Los fields (as como el resto de las expresiones) se definen en la ventana "Report Inspector" a la izquierda del diseador del reporte. Ah existe un nodo llamado "Fields" que es donde se encuentran los fields que hemos definido y que por lo tanto podemos usar ^-^.

Hacemos clic derecho en el nodo "Fields" de la ventana "Report Inspector". Con esto se abre un men contextual. Seleccionamos la opcin "Add Field" (la nica habilitada).

Con esto se agregar un field llamado "field1" que por default es de tipo "java.lang.String". Cambiamos el nombre de este campo por "nombre" usando la ventana de propiedades. Esto es importante porque donde pongamos este campo ser en donde se muestre el valor del campo nombre de nuestros objetos Participantes.

Hacemos

lo

mismo

para

el

resto

de

los

campos

("username",

"password", "comentarios", "id"). Hay que tener cuidado cuando agreguemos el field "id". Si recuerdan, en la definicin de la clase "Participante" la propiedad "id" est definida como "int". Sin

embargo, a JasperReports solo podemos pasarle objetos para ser usados como valores, por lo que ser necesario cambiar el tipo de la clase ("Field Class en la ventana de propiedades) a "java.lang.Integer" (aunque en realidad no mostraremos el "id" en este ejemplo).

Ahora debemos indicar en qu parte de nuestro reporte queremos que se muestren los valores correspondientes a los atributos de los objetos "Participante" que le pasemos. Para eso simplemente arrastramos los fields correspondientes al lugar en el que queremos que se muestren (arrastramos el field "nombre" a donde queremos que se muestre el atributo "nombre", el campo "username" en donde queremos que se muestre el atributo "username", etc.) desde el "Report Inspector". Al arrastrar los fields, iReport agregar automticamente un encabezado para este field, solo borren el encabezado agregado =).

Estos fields los colocaremos en la banda "Detail 1" la cual se repite cada vez que recibe un nuevo objeto y coloca sus valores en la misma fila (quedar ms claro cuando ejecutemos el ejemplo). Al final, el reporte debe quedar de la siguiente forma:

Por el momento esto es todo lo que necesitamos hacer en iReport para mostrar este reporte bsico. Hacemos clic en el botn "Compile Report" para compilar el reporte y generar su archivo ".jasper" respectivo. Podemos ver una vista previa del reporte haciendo clic en la pestaa "Preview". Asegrense de seleccionar el "Empty datasource" antes de ver el preview, de lo contrario les aparecer un error indicado que el documento no tiene pginas. Al final deben ver algo como esto:

Los "nulls" aparecen porque no se recuper ningn valor que coincidiera con el nombre de ese field usando nuestro datasource (que en este caso est vaco, lo cual solucionaremos a continuacin). Regresamos al NetBeans y ahora crearemos una clase que implemente la interface "JRDataSource" de la que habl antes. Esta clase ser la que usaremos como datasource para nuestro reporte, y la que regresar los valores correspondientes a cada uno de los fields que creamos antes.

Primero agregamos a nuestro proyecto la libreria "JasperReports" que creamos en el primer tutorial de la serie de JasperReports. En esta ocasin no necesitaremos agregar el jar con el Driver de la base de datos, ya que no usaremos ninguna ^_^.

Creamos una nueva clase llamada "ParticipantesDatasource" y hacemos que esta nueva clase implemente la interface "JRDataSource". Podemos hacer que NetBeans implemente de forma automtica los mtodos de esta interface (proporcionando una implementacin vaca) presionando las teclas "Alt + Insert" y seleccionando la opcin "Implement Method" en el men contextual que se abre.

Con esto se abrir una ventana llamada "Generate Implement Methods" en la cual seleccionamos la interface "JRDataSource" para que se seleccionen de forma automtica todos sus mtodos y hacemos clic en el botn "Generate".

Con esto tendremos los dos mtodos implementados y en cuyos cuerpos solo habr:

throw new UnsupportedOperationException("Not supported yet.");

Ahora agregaremos a nuestra clase un atributo de tipo "java.util.List" llamado "listaParticipantes" que mantendr justamente eso: la lista de los participantes de los cuales mostraremos los datos en el reporte. Inicializamos esta lista a un objeto de tipo "java.util.ArrayList", de esta forma:

private

List<Participante>

listaParticipantes

new

ArrayList<Participante>();

Tambin agregamos un contador llamado "indiceParticipanteActual"

de tipo "int", que usaremos enseguida, y lo inicializamos a "-1"; un poco ms adelante explicar por qu este valor:

private int indiceParticipanteActual = -1;

Hasta ahora nuestra clase debe verse as:

public class ParticipantesDatasource implements JRDataSource { private List<Participante> listaParticipantes = new

ArrayList<Participante>(); private int indiceParticipanteActual = -1;

public Object getFieldValue(JRField jrf) throws JRException { throw new UnsupportedOperationException("Not supported yet."); }

public boolean next() throws JRException { throw new UnsupportedOperationException("Not supported yet."); } }

Ahora implementaremos el mtodo getFieldValue. Este mtodo recibe un argumento de tipo JRField. Este parmetro nos indicar por cul de los "fields" nos est preguntando el reporte. En la implementacin de este mtodo regresaremos los valores correspondientes a cada uno de los atributos de nuestros objetos

"Participante" conforme se vayan pidiendo. Por lo que el mtodo queda as:

public Object getFieldValue(JRField jrField) throws JRException { Object valor = null;

if("nombre".equals(jrField.getName())) { valor listaParticipantes.get(indiceParticipanteActual).getNombre(); } else if("username".equals(jrField.getName())) { valor listaParticipantes.get(indiceParticipanteActual).getUsername(); } else if("password".equals(jrField.getName())) { valor listaParticipantes.get(indiceParticipanteActual).getPassword(); } else if("comentarios".equals(jrField.getName())) { valor listaParticipantes.get(indiceParticipanteActual).getComentarios(); } = = = =

return valor; }

Donde bsicamente ocurre lo que dije anteriormente: cada vez que el reporte pregunta por el valor de un atributo del objeto "Participante" actual (al que hace referencia el contador "indiceParticipanteActual") se regresa el valor correspondiente. Esta peticin se hace en base al nombre del field que creamos desde iReport. Ahora tal vez se estn preguntando en qu momento se incrementa el contador?, o cmo sabe JasperReport cuntos participantes existen? Pues bien, ambas cosas ocurren gracias a la implementacin del mtodo "next()".

public boolean next() throws JRException { return ++indiceParticipanteActual < listaParticipantes.size(); }

JasperReport pregunta a este mtodo para saber si existe otro "Participante" en la lista. Este mtodo es el primero que se llama al generar el reporte. Por lo que el contador debe comenzar en "-1", as cuando este mtodo se llama la primera vez el contador queda en " 0", y cuando se invoca al mtodo "getFieldValue" se regresa el objeto Participante del ndice adecuado.

Esto es todo lo que necesitamos para que nuestro datasource funcione para generar reportes. Agregar un mtodo de utilidad a esta clase, llamado "addParticipante", que me permita agregar un nuevo participante a la lista (aunque si lo prefieren pueden agregar el setter de "listaParticipantes"):

public void addParticipante(Participante participante)

{ this.listaParticipantes.add(participante); }

La clase "ParticipantesDatasource" queda de esta forma (omitiendo los imports):

public class ParticipantesDatasource implements JRDataSource { private List<Participante> listaParticipantes = new

ArrayList<Participante>(); private int indiceParticipanteActual = -1;

public Object getFieldValue(JRField jrf) throws JRException { Object valor = null;

if ("nombre".equals(jrf.getName())) { valor listaParticipantes.get(indiceParticipanteActual).getNombre(); } else if ("username".equals(jrf.getName())) { valor listaParticipantes.get(indiceParticipanteActual).getUsername(); } else if ("password".equals(jrf.getName())) { = =

valor listaParticipantes.get(indiceParticipanteActual).getPassword(); } else if ("comentarios".equals(jrf.getName())) { valor listaParticipantes.get(indiceParticipanteActual).getComentarios(); }

return valor; }

public boolean next() throws JRException { return ++indiceParticipanteActual < listaParticipantes.size(); }

public void addParticipante(Participante participante) { this.listaParticipantes.add(participante); } }

Para finalizar agregaremos en nuestra clase "Main" un ciclo dentro del cual crearemos 10 objetos "Participante", los cuales iremos agregando uno a uno a un objeto de tipo "ParticipantesDatasource":

ParticipantesDatasource datasource = new ParticipantesDatasource();

for (int i = 1; i <= 10; i++) {

Participante p = new Participante(i, "Particpante " + i, "Usuario " + i, "Pass " + i, "Comentarios para " + i); datasource.addParticipante(p); }

El resto del cdigo para generar el reporte es similar al del primer tutorial de JasperReports, solo que en esta ocasin en lugar de pasar un objeto de tipo "Connection" al mtodo "fillReport" del "JasperFillManager" pasamos nuestro objeto "ParticipantesDatasource" de esta forma:

JasperPrint datasource);

jasperPrint

JasperFillManager.fillReport(reporte,

null,

Al final el cdigo de nuestra clase "Main" queda as:

public class Main { public static void main(String[] args) throws Exception { ParticipantesDatasource ParticipantesDatasource(); datasource = new

for (int i = 1; i <= 10; i++) { Participante p = new Participante(i, "Particpante " + i, "Usuario " + i, "Pass " + i, "Comentarios para " + i); datasource.addParticipante(p); }

JasperReport

reporte

(JasperReport)

JRLoader.loadObject("reporte2.jasper"); JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, null, datasource);

JRExporter exporter = new JRPdfExporter(); exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); exporter.setParameter(JRExporterParameter.OUTPUT_FILE, java.io.File("reporte2PDF.pdf")); exporter.exportReport(); } } new

Al ejecutar este cdigo debemos terminar con un archivo llamado "reporte2PDF.pdf" en el directorio raz de nuestro proyecto y que tiene el siguiente contenido:

Podr no ser el reporte ms bello o ms til del mundo, pero es nuestro =D. Podemos ver que los datos importantes (los datos de los "Participantes") aparecen en la banda "Details 1" (donde colocamos los fields) y que esta banda se repite por cada uno de los participantes de la lista.

Bien, con esto vemos que podemos crear nuestros propios datasources y pasar datos para generar reportes sin la necesidad de una conexin a base de datos. Es bastante sencillo, solamente debemos proporcionar una clase que implemente la interface "JRDataSource" y que regrese los valores correspondientes a los fields del reporte.

Aunque esto ya es bastante fcil y til, existe una forma an ms simple para crear un datasource, sin la necesitad de implementar la interface "JRDataSource". Esto es gracias a un conjunto de clases que JasperReports ya nos proporciona y realizan bsicamente la misma funcin que la clase "ParticipantesDatasource" que acabamos de crear. Estas clases son:

JRBeanCollectionDataSource JRJpaDataSource JRBeanArrayDataSource

Las cuales ya implementan la interface "JRDataSource". Cada una funciona con distintos tipos de datos, pero en que nos interesa en este momento es "JRBeanCollectionDataSource" que puede convertir una "java.util.Collection" en un DataSource de forma automtica. Modificaremos nuestro mtodo "main" para hacer uso de esta clase. Ahora, en vez de llenar nuestro "ParticipantesDatasource" en el ciclo simplemente llenaremos un "java.util.List":

List listaPariticipantes = new ArrayList();

for (int i = 1; i <= 10; i++) { Participante p = new Participante(i, "Particpante " + i, "Usuario " + i, "Pass " + i, "Comentarios para " + i); listaPariticipantes.add(p); }

Y en lugar de pasar un objeto de tipo "Connection" al mtodo "fillReport" del "JasperFillManager" pasamos un nuevo objeto "JRBeanCollectionDataSource" construido con la lista "listaPariticipantes":

JasperPrint

jasperPrint

JasperFillManager.fillReport(reporte,

null,

new JRBeanCollectionDataSource(listaPariticipantes));

Al final nuestra clase "Main" queda as:

public class Main { public static void main(String[] args) throws Exception { List listaPariticipantes = new ArrayList();

for (int i = 1; i <= 10; i++) {

Participante p = new Participante(i, "Particpante " + i, "Usuario " + i, "Pass " + i, "Comentarios para " + i); listaPariticipantes.add(p); }

JasperReport

reporte

(JasperReport)

JRLoader.loadObject("reporte2.jasper");

JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, null, new JRBeanCollectionDataSource(listaPariticipantes));

JRExporter exporter = new JRPdfExporter(); exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); exporter.setParameter(JRExporterParameter.OUTPUT_FILE, java.io.File("reporte2PDF_2.pdf")); exporter.exportReport(); } } new

Al ejecutar este cdigo se genera el archivo "reporte2PDF_2.pdf" en el directorio raz de nuestro proyecto, con el siguiente contenido:

El cual es idntico al "ParticipantesDatasource".

reporte

generado

con

la

clase

Y con esto vemos que gracias a la clase "JRBeanCollectionDataSource" no es necesario que proporcionemos nuestra propia clase que implemente la interface "JRDataSource", por lo que la generacin de reportes es an ms simple ^-^.

Bien, esto es todo por ahora. Espero que este post les sea de utilidad. En el siguiente tutorial hablar de cmo funcionan los parmetros y las variables en JasperReport, para lo cual haremos uso del reporte que acabamos de crear.

Gracias a todos y no olviden dejar sus dudas, comentarios, y sugerencias. Todo es bienvenido. Saludos. Descarga los archivos de este tutorial desde aqu:

Reporte con Datasource Propio

Вам также может понравиться