Using JasperReports with Hibernate

Version 1

    This section will demonstrate a couple of ways to use the results of a Hibernate query as the datasource of a report in JasperReports.

    When the Hibernate query returns a simple collection of objects, The  JRBeanCollectionDataSource class in the JasperReports library will work fine.

    List cats = session.find("from eg.Cat");
    
    Map parameters = new HashMap();
    parameters.put("Title", "The Cat Report");
    
    InputStream reportStream = this.class.getResourceAsStream("/the-cat-report.xml");
    JasperDesign jasperDesign = JasperManager.loadXmlDesign(reportStream);
    JasperReport jasperReport = JasperManager.compileReport(jasperDesign);
    
    JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(cats);
    JasperPrint jasperPrint = JasperManager.fillReport(jasperReport, parameters, ds);
    
    JasperManager.printReportToPdfFile(jasperPrint, "the-cat-report.pdf");
    

     

    However, when the Hibernate query returns tuples of objects (each tuple as an array, each array as an element in the returned List), things get a little tricky. Jasper needs a way to reference each object in the array by a field name. This class is a good solution, but at this time you are required to pass an array of field names matching the results of the query.

    public class HibernateQueryResultDataSource implements JRDataSource { 
    
      private String[] fields; 
      private Iterator iterator; 
      private Object currentValue; 
    
      public HibernateQueryResultDataSource(List list, String[] fields) { 
        this.fields = fields; 
        this.iterator = list.iterator(); 
      } 
    
      public Object getFieldValue(JRField field) throws JRException { 
        Object value = null; 
        int index = getFieldIndex(field.getName()); 
        if (index > -1) { 
          Object[] values = (Object[])currentValue; 
          value = values[index]; 
        } 
        return value; 
      } 
    
      public boolean next() throws JRException { 
        currentValue = iterator.hasNext() ? iterator.next() : null; 
        return (currentValue != null); 
      } 
    
      private int getFieldIndex(String field) { 
        int index = -1; 
        for (int i = 0; i < fields.length; i++) { 
          if (fields[i].equals(field)) { 
            index = i; 
            break; 
          } 
        } 
        return index; 
      } 
    
    }
    

     

    Now running your report would look something like this:

    List cats = session.find("select cat.type, cat.birthdate, cat.name from eg.DomesticCat cat");
    
    Map parameters = new HashMap();
    parameters.put("Title", "The Cat Report");
    
    InputStream reportStream = this.class.getResourceAsStream("/the-cat-report.xml");
    JasperDesign jasperDesign = JasperManager.loadXmlDesign(reportStream);
    JasperReport jasperReport = JasperManager.compileReport(jasperDesign);
    
    String[] fields = new String[] { "type", "birthdate", "name"};
    HibernateQueryResultDataSource ds = new HibernateQueryResultDataSource(cats, fields);
    JasperPrint jasperPrint = JasperManager.fillReport(jasperReport, parameters, ds);
    
    JasperManager.printReportToPdfFile(jasperPrint, "the-cat-report.pdf");
    

    Another, alternate implementation, from Erik Romson:

     

    public class HibernateQueryResultDataSource implements JRDataSource
    {
        private static final transient Logger logger = 
            Logger.Factory.getInstance(HibernateQueryResultDataSource.class);
        protected HashMap fieldsToIdxMap=new HashMap();
        protected Iterator iterator;
        protected Object currentValue;
        List values;
    
        public HibernateQueryResultDataSource(List list, String query)
        {
            int start =query.indexOf("select ");
            int stop =query.indexOf(" from ");
            Assertion.assertTrue(
               (start!=-1) && 
               (stop!=-1),
               "The query "+
               query+
               " must be of the form select x,x from ..."
            );
            start+="select".length();
            String parameters=query.substring(start,stop);
            parameters=parameters.trim();
            Assertion.assertTrue(
                parameters.length()>0,
                "The query "+
                query+
                " seems to be weird"
            );
            StringTokenizer tokenizer=new StringTokenizer(parameters,",");
            int idx=0;
            while( tokenizer.hasMoreTokens() )
            {
                String parameter=tokenizer.nextToken();
                fieldsToIdxMap.put( parameter.trim(), new Integer(idx) );
                idx++;
            }
            values=list;
            this.iterator = list.iterator();
        }
    
        public Object getFieldValue(JRField field) throws JRException
        {
            String fieldName=field.getName().replace('_','.'); 
            //JasperReports does not allow '.' in field names, so we 
            //use '_' for nested properties instead and must convert here 
            //(comment added by kbrown)
            Integer idxInt=(Integer) fieldsToIdxMap.get(fieldName);
            if (idxInt==null)
            {
                Assertion.fail(
                    "The field \""+
                    fieldName+
                     "\" did not have an index mapping. Should be one off "+
                    fieldsToIdxMap.keySet()
                );
            }
            Object[] values = (Object[]) currentValue;
            Assertion.assertTrue(
                idxInt.intValue()<values.length,
                "The index from field "+
                fieldName+
                " was illegal"
            );
            Object value= values[ idxInt.intValue() ];
            if ( logger.isDebugEnabled() )
            {
                logger.debug("fetched value "+value+" from field "+fieldName);
            }
            return value;
        }
    
        public boolean next() throws JRException
        {
            currentValue = iterator.hasNext() ? iterator.next() : null;
            return currentValue != null;
        }
    
        public List getValues()
        {
            return values;
        }
    }
    

    Using the information from this page I've made a patch to JasperReports 0.6.5. You can find the patch at: http://www.waltermourao.com.br/JasperReports/jr65_hql.zip or you can download the full version from: http://www.waltermourao.com.br/JasperReports/jasperreports-hql-0.6.5.zip