lunes, 11 de junio de 2007

Reportes con JasperReports

Para la generación de reportes estudié e implementé JasperReport. Esta es una herramienta libre y 100% escrito en Java. Los reportes pueden contener listados, gráficos, imágenes, subreportes, entre otros.

Hay dos formas que utilicé para realizar un reporte con DataSource de Hibenate. La primera (la que explico detalladamente a continuación) es con una Hibernate connection directa y la otra es enviando el mismo List que retorna el query, hacia el reporte.

El proceso de generación de un reporte con una Hibernate Connection es el siguiente:

  1. Se debe crear un archivo .jrxml (de la familia de xml basado en el dtd de JasperReport). Esto lo hará iReport. Lo podemos encontrar en http://jasperforge.org -> [iReport -> Download].

    Al abrirlo nos mostrará un entorno para la fabricación de un reporte

    Aquí tenemos que configurar algunas cosas:

  • Ruta de los programas para visualizar el reporte: Options -> Settings -> External Programs.

  • Options -> settings -> Compiler . Activamos aquí, sino la carpeta donde están los binarios se nos llenará de archivos temporales

  • Options -> Classpath : Esto en muy importante ya que le indicaremos donde tiene que buscar los recursos.

    Aquí se deben agregar las carpetas donde se encuentra todos los archivos de mapeo, los java, además del driver manager jdbc de postgres

  • Luego definimos el data source del reporte: Data -> Connections/Data Sources y creamos un HibernateConnection.

  • Listo. Para comenzar crearemos un reporte con el asistente e ingresamos el hql.

  • Seguimos click click click .. En :Build -> [ pdf | html | xls | etc] seleccionamos el tipo de reporte

  • Para ver final mente el reporte: Build -> Execute (with a active connection). Al guardarlo veremos lo ha hecho en un archivo jrxml.

  • Podemos apreciar la estructura del reporte:

    • Title : Título del reporte. Lo pone solo en la primera página del reporte

    • PageHeader : Pié de página superior. Aquí podría ir el nombre del usuario que lanzó e reporte.

    • ColumnHeader : Nombre de la columna. Se repite en todas las páginas.

    • Detail : Detalle. No dejar aquí objetos, ej. un gráfico, por que puede repetirse (todo el obj.)

    • ColumnFooter : Algún detalle al final de la columna. ej. Suma total.

    • PageFooter : Pié de página. número de hoja, hora, etc

    • LastPageFooter : Este pié de pagina solo va en la última página

    • Summary : Este espacio va entre el ColumnFooter y PageFooter. y se repite en todas las págs.

  • Luego podremos personalizar el diseño del reporte. Lo principal se encuentra aquí:

    • Fields: estas son las columnas que nos trajo la consulta

      Nota: para modificar la consulta: Data -> Report query. No podemos hacer consultas con join fetch, pero podemos agregar las columnas que faltan, navegando por el árbol de relaciones que aparece al lado y pinchar en Add Selecte

    • Variables: Son variables generadas por el propio iReport. ej el número de página, número de columna, etc.

    • Parameters: estos parámetros son enviados desde el proyecto hacia el reporte. ej. si queremos que en el PageHeader aparezca el nombre del proceso de postulación, creamos un parámetro (en iReport) ej. procesoPostulacion de tipo java.lang.String y en el código se lo enviamos así:




Map parameters = new HashMap();
parameters.put("procesoPostulacion", procesoPost);


  1. Con el jrxml dentro del proyecto nos queda agregar las siguientes librerías al mismo:

    • iReport.jar

    • itext-2.0.0.jar

    • jasperreports-1.3.1.jar

    • jfreechart-1.0.5

    • jcommon-1.0.0.jar

    • poi-2.5.1-final-20040804.jar

  • Para finalizar la prueba creamos un botón jsp que ejecute un método como el siguiente.




    <af:commandbutton id="reporte" text="Reporte" action="#{institucionesBean.reporte}">
    <af:setactionlistener from="#{prmGlobal.tipoPdf}" to="#{institucionesBean.tipoReporte}">


    public String reporte() {
    try{
    URL url = this.getClass().getResource("/reportes/instituciones.jrxml");
    String jrxml = url.getPath();
    Map parameters = new HashMap();
    JasperReportsToBrowser.reportConnection(jrxml, parameters, tipoReporte);
    }catch(Exception e){
    e.printStackTrace();
    }
    return null;
    }


    </af:setactionlistener></af:commandbutton>



      • <af:setActionListener from="#{prmGlobal.tipoPdf}" to="#{institucionesBean.tipoReporte}" />

        Este parámetro indica el formato del reporte; hasta el momento está "tipoPdf" y "tipoExcel"

        tipoReporte es una variable String de la clase del Backing.

      • String jrxml = this.getClass().getResource( "/reportes/instituciones.jrxml");

        es la ruta completa del jrxml

      • Map parameters = new HashMap(); con este hasMap agregamos los parámetros que definimos en el jrxml.

      • JasperReportsToBrowser.reportConnection(jrxml, parameters,tipoReporte);

        He creado esta clase y método estático y la he agregado al paquete util, donde se completan los pasos en la generación del reporte y entregarlo a través del navegador. Se le debe pasar cadena que contiene el path completo (del sistema) del archivo jrxml; y el hashMap al que le hemos agregado los parámetros que recibirá el reporte.



    Reportes con gráficos.



    Para generar reportes con gráficos, hacemos click aquí , luego lo dibujamos en el reporte y lo localizaremos en el summary. Para graficar necesitaremos dos variables, para este ejemplo utilicé instituciones, así que crearé la variable $F{Tipo} que representa los tipos de instituciones (colegio, salud, administrativo, iglesia, etc) y la variable $F{Total}, que contiene el total de cada tipo.




    Vamos a ingresar la consulta en Data -> Report Query:

    select ti.nombre as Tipo, count(*)as Total

    from Institucion i ,Tipoinstitucion ti

    where i.tipoinstitucion = ti.codigo

    group by ti.nombre


    Salimos y vamos a las propiedades del gráfico -> chart -> edit chart properties -> char data -> details e ingresamos las variables.


    Guardamos e incorporamos el reporte al proyecto.


    El proceso de generación de reporte por medio de un List.

    Para este método tenemos que crear un jrxml de la misma forma como se menciono anteriormente, de hecho, podemos utilizar el mismo, la diferencia está en el método.




    public String reporte() {
    try{
    URL url = this.getClass().getResource("/reportes/instituciones.jrxml");
    String jrxml = url.getPath();
    List list = InstitucionCT.getInstituciones(campoInstitucion,tipoInstitucion,null,codestructuraiasd);
    Map parameters = new HashMap();
    JasperReportsToBrowser.reportList(jrxml, parameters,list,tipoReporte);
    }catch(Exception e){
    e.printStackTrace();
    }
    return null;
    }


    Como se puede apreciar se envía la misma consulta que se solicita para mostrar el listado, que se aprecia en el ABM de Institución.

    JasperReportsToBrowser.reportList(jrxml, parameters, list,tipoReporte);


    Este método es el que construye el reporte basado en el listado y lo sirve a través del navegador.




  • Adjunto la clase JasperReportsToBrowser

    /*
    * Genera y entrega el reporte a través del browser
    * @author Rene Vielma
    *
    */
    package unach.util;

    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.sql.Connection;
    import java.util.List;
    import java.util.Map;

    import javax.faces.context.ExternalContext;
    import javax.faces.context.FacesContext;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletResponse;

    import net.sf.jasperreports.engine.JRException;
    import net.sf.jasperreports.engine.JasperCompileManager;
    import net.sf.jasperreports.engine.JasperExportManager;
    import net.sf.jasperreports.engine.JasperFillManager;
    import net.sf.jasperreports.engine.JasperPrint;
    import net.sf.jasperreports.engine.JasperReport;
    import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
    import net.sf.jasperreports.engine.export.JRXlsExporter;
    import net.sf.jasperreports.engine.export.JRXlsExporterParameter;

    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    import org.hibernate.Transaction;

    import unach.hibernate.SessionFactory;
    import unach.util.beans.ParametrosGlobales;


    public class JasperReportsToBrowser extends HttpServlet{

    public static ParametrosGlobales prmGlobal;

    // Methods

    /**
    * Crea un reporte y lo sirve a través del browser
    * @param path
    * @param parameters
    * @param tipo
    * @throws ServletException
    * @throws IOException
    */
    public static void reportConnection(String path,Map parameters,String tipo) throws ServletException, IOException{

    HttpServletResponse response = null;
    Session session = null;
    Transaction tx = null;

    try
    {
    session = null;
    tx = null;
    session = SessionFactory.getSession();
    tx = session.beginTransaction();

    Connection connection = SessionFactory.getSession().connection();

    parameters.put("HIBERNATE_SESSION", session);

    JasperReport report = JasperCompileManager.compileReport(path);
    JasperPrint print = JasperFillManager.fillReport(report,parameters,connection);

    ExternalContext ectx = FacesContext.getCurrentInstance().getExternalContext();
    response = (HttpServletResponse)ectx.getResponse();
    OutputStream out = response.getOutputStream();

    if(tipo == prmGlobal.getTipoPdf()){

    byte[] bytes = JasperExportManager.exportReportToPdf(print);

    response.setHeader("Content-disposition", "attachment; filename=ListadoPDF");
    response.setContentType(tipo);
    //response.setHeader("Cache-Control", "private");
    //response.setHeader("Pragma", "");
    response.setContentLength(bytes.length);
    out.write(bytes);
    out.flush();
    out.close();
    }

    if(tipo == prmGlobal.getTipoExcel()){

    ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();

    JRXlsExporter exporterXLS = new JRXlsExporter();

    exporterXLS.setParameter(JRXlsExporterParameter.JASPER_PRINT, print);
    exporterXLS.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, arrayOutputStream);
    exporterXLS.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
    exporterXLS.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);
    exporterXLS.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
    exporterXLS.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
    exporterXLS.exportReport();


    response.setHeader("Content-disposition", "attachment; filename=ListadoPDF");
    response.setContentType("application/vnd.ms-excel");
    //response.setHeader("Cache-Control", "private");
    //response.setHeader("Pragma", "");
    response.setContentLength(arrayOutputStream.toByteArray().length);
    out.write(arrayOutputStream.toByteArray());
    out.flush();
    out.close();

    }


    FacesContext.getCurrentInstance().responseComplete();

    }
    catch (JRException e){

    // display stack trace in the browser
    StringWriter stringWriter = new StringWriter();
    PrintWriter printWriter = new PrintWriter(stringWriter);
    e.printStackTrace(printWriter);
    response.setContentType("text/plain");
    response.getOutputStream().print(stringWriter.toString());
    }

    catch (HibernateException e) {
    if (tx != null)
    tx.rollback();
    throw e;
    }

    finally {
    session.close();
    }
    }
    /**
    * Envía el List en el reporte y lo sirve a través del browser
    * @param path
    * @param parameters
    * @param list
    * @throws ServletException
    * @throws IOException
    * @author Rene Vielma
    */
    public static void reportList(String path,Map parameters,List list,String tipo) throws ServletException, IOException{

    HttpServletResponse response = null;

    try{
    JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(list);

    JasperReport report = JasperCompileManager.compileReport(path);
    JasperPrint print = JasperFillManager.fillReport(report,parameters,ds);

    ExternalContext ectx = FacesContext.getCurrentInstance().getExternalContext();
    response = (HttpServletResponse)ectx.getResponse();
    OutputStream out = response.getOutputStream();

    if(tipo == prmGlobal.getTipoPdf()){

    byte[] bytes = JasperExportManager.exportReportToPdf(print);

    response.setHeader("Content-disposition", "attachment; filename=ListadoPDF");
    response.setContentType(tipo);
    //response.setHeader("Cache-Control", "private");
    //response.setHeader("Pragma", "");
    response.setContentLength(bytes.length);
    out.write(bytes);
    out.flush();
    out.close();
    }

    if(tipo == prmGlobal.getTipoExcel()){

    ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();

    JRXlsExporter exporterXLS = new JRXlsExporter();

    exporterXLS.setParameter(JRXlsExporterParameter.JASPER_PRINT, print);
    exporterXLS.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, arrayOutputStream);
    exporterXLS.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
    exporterXLS.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);
    exporterXLS.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
    exporterXLS.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
    exporterXLS.exportReport();


    response.setHeader("Content-disposition", "attachment; filename=ListadoPDF");
    response.setContentType("application/vnd.ms-excel");
    //response.setHeader("Cache-Control", "private");
    //response.setHeader("Pragma", "");
    response.setContentLength(arrayOutputStream.toByteArray().length);
    out.write(arrayOutputStream.toByteArray());
    out.flush();
    out.close();

    }

    FacesContext.getCurrentInstance().responseComplete();
    }
    catch (JRException e){

    // display stack trace in the browser
    StringWriter stringWriter = new StringWriter();
    PrintWriter printWriter = new PrintWriter(stringWriter);
    e.printStackTrace(printWriter);
    response.setContentType("text/plain");
    response.getOutputStream().print(stringWriter.toString());
    }
    }

    }

    7 comentarios:

    Anónimo dijo...

    Este metodo como esta hecho????
    Donde lo obtengo
    JasperReportsToBrowser

    Rene Vielma dijo...

    lo he adjuntado al final

    Anónimo dijo...

    Gracias, de verdad que me ha servido mucho la información

    Anónimo dijo...

    Hola, tengo un problema... no puede lograr establecer la conexion con hibernate desde el ireport...
    Para mi el problema esta en los classpath que debo agregar...
    Cuales deben ser?? me los podrias detallar?
    Yo por ejemplo agregue: el jar del potgres; la carpeta donde esta "hibernate.cfg.xml" y los archivos de cada mapeo; y la carpeta donde estan las clases de entidad compiladas?
    Que estoy haciendo mal??

    Gracias, salu2

    Rene Vielma dijo...

    Según recuerdo en esa ocasión no le di muchas vueltas al asunto y agregué todas las rutas del proyecto directamente en el archivo de configuración :
    $ vi /home/rene/.ireport/config.xml
    .
    .
    ...![CDATA[/usr/local/jdk1.5.0_11/jre/lib/ext/postgresql-8.1-405.jdbc3.jar
    /home/rene/workspace/sigaa
    /home/rene/workspace/sigaa/WebRoot
    /home/rene/workspace/sigaa/WebRoot/META-INF
    /home/rene/workspace/sigaa/WebRoot/WEB-INF/classes
    /home/rene/workspace/sigaa/WebRoot/WEB-INF/classes/reportes
    .
    .

    con una pequeña ayuda de:
    tree -dfin /home/rene/nombreProyecto

    pd: Lo intenté hacerlo con el mouse (con el asistente de iReport), pero me stresó.

    Espero te sirva de ayuda

    Unknown dijo...

    Hola, estoy realizando una aplicacion en Netbeans y genero mis reportes con iReport. Todo me funciona bien. La pregunta es si es posible que desde la misma aplicacion yo pueda enviar el "Query" como un parametro para lo que va a mostrar el reporte, es decir, recibirlo desde el reporte de iReport y generarlo con ese query. Lo que trato de hacer es generar reportes de la misma forma o la misma plantilla pero con diferentes query. Las consulas las hago asi: "Select * from presupuestos1", y asi tantos presupuestos como tablas, esto significa tantos archivos de reportes .jrxml como tablas tenga

    Jose Angel Labbad dijo...

    Estoy trabajando con netbeans 6.7, ya genere el reporte que queria con report wizzard, lo modifique un poco y quede muy satisfecho con los resultados. ahora solo debo saber como hacerpara que haciendo click en un boton de un jframe de netbeans, pueda generarse y abrirse automaticamente un pdf con el reporte. los post que estoy consiguiendo por internet no son nada claros.

    ES MUY IMPORTANTE PARA MI LOGRAR ESTO, agradeceria un mundo al que sepa explicarme detalladamente.GRACIAS!.