TNT blog  
日历
<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
统计
  • 随笔 - 5
  • 文章 - 40
  • 评论 - 7
  • 引用 - 0

导航

常用链接

留言簿(2)

随笔分类

随笔档案

文章分类

文章档案

收藏夹

home

搜索

  •  

最新随笔

最新评论

阅读排行榜

 

JasperReport和iReport是不错的Java报表工具. 在实际项目中, 本人用它们开发了20个Report, 涉及SubReport, Image, Graph, 积累了一些经验. 尤其是关于Export到Excel方面, 文档上也很少提及, 纯粹是摸索出来的, 有的问题还是通过读源代码才解决的. 此贴并非入门教程, 差不多算是笔记吧, 以问答形式记录. 

 

     

  1. iReport

     

  2. 安装

     

       

    1. 下载,解压iReport 0.4.0 (推荐src版本)

       

    2. 确认JDK是1.4以上

       

    3. 把JDK /lib下的tools.jar拷贝到{ireport_home}/lib目录中

     

  3. 运行

     

       

    1. 对于下载的Binary版本,只能运行/bin/startup.bat

       

    2. 对于下载的Src版本,可以通过ant iReport运行(先安装ant)

       

    3. 如果运行startup.bat,出现java.lang.NoSuchMethodError错误,一般是JDK版本太低。如果确认已安装了1.4或以上,检查path系统变量,看看有1.3的JRE是不是排在前面(比如安装了Oracle的客户端,往往有1.3的JRE),如果出现Class Not Found,检查classpath。对于通过ant的方式运行,一般都没什么问题,所以推荐下载src版本

     

  4. JasperReport 常见问题

     

       

    1. .jrxml vs .jasper

       

         

      • 如果在运行时载入.jrxml, 那么每次调用还得编译, 不如预先编译成.jasper.不过预先编译的jasper,必须用同样版本的JasperReport载入,而且灵活性差些. 不过对于大部分报表,还是预先编译成jasper方便

       

    2. 如果批量编译jrxml

       

         

      • 用Ant很容易解决

         

        .....

         

       

    3. 如何使用图片?

       

         

      • 很容易,用Image控件就可以了. 在Image Express里面可以用String来表示图片的路径, 或者用InputStream, File对象.不过不管用File还是String对象, 都不得不用绝对路径, 这显然很不灵活. 解决办法是,穿入一个$P的参数,表示图片所在的目录,然后用$P和文件名拼接出完整的绝对路径. 更好的方法是用InputStream, 例如this.getClass().getResourceAsStream("logo.jpg") ,这时只要把图片放在当前.jasper所在的目录就可以了,不必考虑什么参数,什么路径了

       

    4. 显示非数据库字段变量

       

         

      • 显示如运行日期等,可以直接在Text Field里面输入new java.util.Date(), 然后把Pattern设成如mm/dd/yyyy.

       

    5. 动态控制某些Field是否显示

       

         

      • 每个Static Text, Text Field甚至整个Band的属性里面都有Print When Expression, 比如设成new Boolean(!$P{isDisplay}.equalsIgnoreCase("yes")), 那么只有当参数display的值为yes的时候才显示

       

    6. 使用Sub Report, 如何使用相对路径

       

         

      • 见1.3, 和使用图片类似, 用InputStream或者传入参数

       

    7. Query里面如何使用参数

       

         

      • $P!{xxx} 或者 $P{xxx} 后者只能用于类似PreparedStatement参数绑定, 而前者可替换Sql的任意部分. 在需要动态排序的时候, 前者特别有用. 比如 select a,b,c from t order by $P!{orderClause}   不管用$P还是$P!, SQL最终是以PreparedStatement方式执行的, 不必太担心性能问题   注意:参数是不能嵌套的, 比如$P{a} =''$P{b}''   , $P{b}=''value'', 不要指望$P{a}能被替换成''value''

       

    8. 如何使用图表(Graph)

       

         

      • JasperReport本身没有图表功能, 只有显示Image的功能(见4.3). iReport里有个Graph向导, 其实质是通过jFreeChart生成Image. 更另外, 更直接的做法是放一个Image控件, Image Express Class设置成java.awt.Image, 在Image Expression里通过自定义的类返回java.awt.Image对象. 例如''GraphProvider.getImage($P{REPORT_DATASOURCE},title, subtitle.....)''.  GraphProvider是自己的类, public static Image getImage(JRDataSource, ....)

       

    9. 如果显示多个图表

       

         

      • 在一张报表上显示一个图表和显示多个图表是不同的. 假设Query是select name,price,qty from xxx, 第一张图显示name-price, 第二张图显示name-qty, 如果还是按3.8的方法, 第二张图根本显示不出来! 为什么  因为传入的是JRDataSource, 而JRDataSource仅仅是对ResultSet的简单封装, 在第一张图处理完后, 游标已经到了eof位置了, 在开始处理第二张图的时候,就必然抛出游标耗尽的异常! 怎么办   自己写个JRDataSourceAdapter, 把JRDataSource对象里面的值预先保存到一个Collection (相当于一个Offline的数据集), 然后把这个Collection传个getImage方法. 具体是, 建一个Variable  mydate, 类型是java.util.Map, Calculation Type- System, Initial Value Expression是JRDataSourceAdapter.JRDataSource2Map($P{REPORT_DATA_SOURCE},new String[]{"NAME","PRICE","QTY"},new Class[]{java.lang.String.class,java.lang.Double.class,java.lang.Double.class}), JRDataSource2Map是自己写的一个Adapter. 然后在Image的Expression里面换成如''GraphProvider.getImage(mydata,title, other params...), 当然得修改getImage方法

     

  5. Export到Excel的问题

     

       

    1. 如何去掉报表头等

       

         

      • 直接把不需要的Band删除(把其高度设为0). 如果仅仅是export到Excel的时候不需要报表头, 而输出到PDF等仍然需要保留, 那么使用print when expression, 见4.4

       

    2. 如果让Excel看起来整齐

       

         

      • 不要有空白地方! 首先把所有的Field设成一样高, 对齐! 把所在Band的高度也设成和Field一样高, 让Field正好放入Band. 然后调整Field的宽度, 让每个Field都相邻,没有空隙. 最后,记得设置参数: exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,
                            Boolean.TRUE);

       

    3. 如何保留GridLine

       

         

      • 首先, 设置参数exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE); 然后,把每个Field或者Static Text框的''Transparent''属性都勾上

       

    4. 如何使字段名只显示一次

       

         

      • 如果把字段名放在ColumnHead区域, 那么输出到Excel, 会每个Page都显示一遍. 在设计Report时候, 一般会设定Page大小. 然而对于Excel, 这个Page设定仍然存在,而且往往很讨厌, 因为在Excel里, 通常希望得到连续的数据, 然而Jasper仍然会''自作多情''进行分页. 比如说, 设计JasperReport的时候, 设定page size为Letter, Portrait, 那么输出到Excel的时候每隔大约30行(具体取决于Field的高度), page header, column header, column foot, page foot 会被重复一次, 而且还附带一个高度为0的Excel Row, 表示Page Break的地方. 把字段名放在title band里, 可以解决字段名重复的问题, 当然page header也不要显示了. 如果需要, 可以把title band的print when expression设成只有输出Excel的时候才显示

       

    5. 为什么Excel里面的数据是从第二行,第B列开始显示的 

       

         

      • 因为第一行和第A列分别是用来表示page top margin 和 page left margin的. 对于Excel来说, 纯粹多余. 解决方法是把page margin 设成0. 不过如果这个report还需要以PDF等显示, 那么设成0就不好看了. 最好能动态的改变page margin. 当然,这个改变只能在外部(调用Report的地方) 进行, 在设计Report的时候是无能为力的. 不幸的是, JasperReport类居然没有setMargin的方法,只有getter. 折中的方法只能是reflect了. 代码示意如下: //use reflect to set the private field of JRBaseReport
                         java.lang.reflect.Field margin = JRBaseReport.class.getDeclaredField(
                                "leftMargin");
                        margin.setAccessible(true);
                        margin.setInt(myRpt, 0);                 margin = JRBaseReport.class.getDeclaredField("topMargin");
                        margin.setAccessible(true);
                        margin.setInt(myRpt, 0);                 margin = JRBaseReport.class.getDeclaredField("bottomMargin");
                        margin.setAccessible(true);
                        margin.setInt(myRpt, 0);

       

    6. 如何去掉Excel中隐藏的行 

       

         

      • 如前说述, 由于page break的关系, Excel中每隔几十行,就有一个高度为0的row, 即使把page botom margin设为0, 把page footer去掉都没有办法. 唯一的解决办法是把page height设为很大. 同5.5一样, 不得不使用reflect:

         

           

        • java.lang.reflect.Field pageHeight = JRBaseReport.class.getDeclaredField(
                                  "pageHeight");
                          pageHeight.setAccessible(true);
                          pageHeight.setInt(myRpt, Integer.MAX_VALUE);

     

  6. 文档

     

       

    1. 哪里有文档 

       

         

      • JasperReport有份Ultimate Guide, 不过不是免费的, 和jFreeChart一个德行. 不过网上有流传, 写的还可以, 60多页, 不过也没详细到哪里去. 如果下载源代码版, 那么看看自带的Demo也不错. SF的论坛也是问问题的最好地方

     

  7. 源代码 仅供参考(reportProvider--一个Servlet, GraphProider, JRDataAdapter都是普通类)

 

/**
 *

 

Title: ReportProviderServlet


 *

 

Description: Servlet to generate Jasper reports


 *

 

Copyright: Copyright (c) 2004


 *

 

Company: *****


 * @author zephyr
 * @version 1.0
 */
package xyz;

 

 

 

import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.base.*;
import net.sf.jasperreports.engine.export.*;
import net.sf.jasperreports.engine.util.*;

 

import org.apache.log4j.*;

 

import java.io.*;

 

import java.sql.*;

 

import java.util.*;

 

import javax.servlet.*;
import javax.servlet.http.*;

 


public class ReportProviderServlet extends HttpServlet
{
    private static Logger log = LogManager.getLogger(ReportProviderServlet.class);

 

    //Initialize: Setup DataSourceManager
    public void init() throws javax.servlet.ServletException
    {
        String prefix = getServletContext().getRealPath("/");
        String file = getInitParameter("data-source-file");

 

        DataSourceManager.configure(prefix + file);

 

        log.info("initialized successfully!");
    }

 

    //Process the HTTP request
    public void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String reportClass = request.getParameter("reportClass");

 

        log.debug("Running Report:" + reportClass);

 

        boolean isExcelFormat = false;

 

        if (reportClass == null)
        {
            throw new IllegalArgumentException("Jasper Class Unspecified");
        }

 

        String reportFormat = request.getParameter("reportFormat");

 

        if (reportFormat == null)
        {
            reportFormat = "jasperPrint";
        }

 

        try
        {
            JasperReport myRpt = JasperManager.loadReport(this.getClass()
                                                              .getResourceAsStream("/jasperReports/" +
                        reportClass + ".jasper"));

 

            //set ReprintHeaderOnEachPage=false for Excel Format
            isExcelFormat = reportFormat.equalsIgnoreCase("excel");

 

            if (isExcelFormat)
            {
                //use reflect to set the private field of JRBaseReport
                //No margin for excel format, max pageHeight
                java.lang.reflect.Field margin = JRBaseReport.class.getDeclaredField(
                        "leftMargin");
                margin.setAccessible(true);
                margin.setInt(myRpt, 0);

 

                margin = JRBaseReport.class.getDeclaredField("topMargin");
                margin.setAccessible(true);
                margin.setInt(myRpt, 0);

 

                margin = JRBaseReport.class.getDeclaredField("bottomMargin");
                margin.setAccessible(true);
                margin.setInt(myRpt, 0);

 

                java.lang.reflect.Field pageHeight = JRBaseReport.class.getDeclaredField(
                        "pageHeight");
                pageHeight.setAccessible(true);
                pageHeight.setInt(myRpt, Integer.MAX_VALUE);

 

                //Don't print group header on each page
                if (null != myRpt.getGroups())
                {
                    for (int i = 0; i < myRpt.getGroups().length; i++)
                    {
                        myRpt.getGroups()[i].setReprintHeaderOnEachPage(false);
                    }
                }
            }

 

            Map params = new HashMap(10);
            Enumeration enu = request.getParameterNames();

 

            while (enu.hasMoreElements())
            {
                String key = (String) enu.nextElement();
                params.put(key,
                    request.getParameter(key).toUpperCase().replaceAll("'", "''"));
                log.debug(key + "=" + request.getParameter(key));
            }

 

            log.debug("Before Filling");

 

            OutputStream httpOut = response.getOutputStream();

 

            Connection conn = DataSourceManager.getConnection(request.getSession());

 

            JasperPrint rptPnt = JasperManager.fillReport(myRpt, params, conn);

 

            conn.close();

 

            if (reportFormat.equalsIgnoreCase("jasperPrint"))
            {
                response.setContentType("application/octet-stream");
                JRSaver.saveObject(rptPnt, httpOut);
            }
            else if (reportFormat.equalsIgnoreCase("pdf"))
            {
                response.setContentType("application/pdf");
                response.setHeader("Content-Disposition",
                    "attachment;filename=\"" + reportClass + ".PDF\"");
                JasperManager.printReportToPdfStream(rptPnt, httpOut);
            }
            else if (reportFormat.equalsIgnoreCase("excel"))
            {
                response.setContentType("application/vnd.ms-excel");
                response.setHeader("Content-Disposition",
                    "attachment;filename=\"" + reportClass + ".XLS\"");

 

                JRXlsExporter exporter = new JRXlsExporter();

 

                exporter.setParameter(JRExporterParameter.JASPER_PRINT, rptPnt);
                exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, httpOut);
                exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,
                    Boolean.TRUE);
                exporter.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET,
                    Boolean.FALSE);
                exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND,
                    Boolean.FALSE);
                exporter.exportReport();
            }
            else if (reportFormat.equalsIgnoreCase("html"))
            {
                JRHtmlExporter exporter = new JRHtmlExporter();
                response.setContentType("text/html");

 

                Map imagesMap = new HashMap();

 

                request.getSession().setAttribute("IMAGES_MAP", imagesMap);

 

                exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP,
                    imagesMap);
                exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI,
                    "image.jsp image=");
                exporter.setParameter(JRExporterParameter.JASPER_PRINT, rptPnt);
                exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, httpOut);

 

                exporter.exportReport();
            }

 

            log.debug("Report Exported");
        }
        catch (Exception ex)
        {
            log.error("Error Occured", ex);
        }
    }
}

 

 

 

 

 

/**
 *

 

Title: JRDataSourceAdapter


 *

 

Description: Converting JRDataSource to Mapped ArrayList


 *

 

Copyright: Copyright (c) 2004


 *

 

Company: *****


 * @author zephyr
 * @version 1.0
 */
package xyz;

 

 

 

import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.design.*;

 

import java.util.*;

 


public class JRDataSourceAdapter
{
    public static Map JRDataSource2Map(JRDataSource dataSource, String[] fieldNames,
        Class[] fieldClasses) throws JRException
    {
        HashMap result;

 

        if (fieldNames.length != fieldClasses.length)
        {
            throw new JRException("Number of Field Name & Class unmatch");
        }

 

        JRDesignField[] fields = new JRDesignField[fieldNames.length];

 

        result = new HashMap(4);

 

        for (int i = 0; i < fieldNames.length; i++)
        {
            fields[i] = new JRDesignField();
            fields[i].setName(fieldNames[i]);
            fields[i].setValueClass(fieldClasses[i]);
            result.put(fieldNames[i], new ArrayList());
        }

 

        do
        {
            for (int i = 0; i < fields.length; i++)
            {
                Object value = dataSource.getFieldValue(fields[i]);
                ((ArrayList) result.get(fields[i].getName())).add(value);
            }
        }
        while (dataSource.next());

 

        return result;
    }
}

 

 

 

/**
 *

 

Title: GraphProvider


 *

 

Description: Generate JFreeChart Image


 *

 

Copyright: Copyright (c) 2004


 *

 

Company: ****


 * @author zephyr 
 * @version 1.0
 */
package xyz;

 

 

 

import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.design.*;
import net.sf.jasperreports.engine.export.*;

 

import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.plot.*;

 

import org.jfree.data.*;

 

import java.awt.*;
import java.awt.image.*;

 

import java.io.*;

 

import java.util.*;

 


public class GraphProvider
{
    public static Image getImage(Map dataSource, String fieldNameX, String fieldNameY,
        String chartName, String titleX, String titleY, boolean isBarChart, int imageWidth,
        int imageHeight) throws JRException
    {
        JRDesignField fieldX = new JRDesignField();
        fieldX.setName(fieldNameX);
        fieldX.setValueClass(java.lang.String.class);

 

        JRDesignField fieldY = new JRDesignField();
        fieldY.setName(fieldNameY);
        fieldY.setValueClass(java.lang.Double.class);

 

        ArrayList periods = (ArrayList) dataSource.get(fieldNameX);
        ArrayList values = (ArrayList) dataSource.get(fieldNameY);

 

        DefaultCategoryDataset categoryDs = new DefaultCategoryDataset();

 

        for (int i = 0; i < values.size(); i++)
        {
            Object obj = values.get(i);
            double dataValue = 0;

 

            if (obj != null)
            {
                dataValue = ((Double) obj).doubleValue();
            }

 

            categoryDs.addValue(dataValue, null, (String) periods.get(i));
        }

 

        JFreeChart c = null;

 

        if (isBarChart)
        {
            c = ChartFactory.createBarChart(chartName, titleX, titleY, categoryDs,
                    PlotOrientation.VERTICAL, false, false, false);
        }
        else
        {
            c = ChartFactory.createLineChart(chartName, titleX, titleY, categoryDs,
                    PlotOrientation.VERTICAL, false, false, false);
        }

 

        c.getTitle().setFont(new Font("Arial", Font.BOLD, 16));

 

        NumberAxis axis = (NumberAxis) c.getCategoryPlot().getRangeAxis();
        axis.setAutoRange(true);

 

        TickUnitSource tickUnits = NumberAxis.createIntegerTickUnits();
        axis.setStandardTickUnits(tickUnits);

 

        return (c.createBufferedImage(imageWidth, imageHeight));

 

       }
}

posted on 2006-06-01 10:36 TNT 阅读(377) 评论(0)  编辑  收藏 所属分类: REPORT

只有注册用户登录后才能发表评论。


网站导航:
 
 
Copyright © TNT Powered by: 博客园 模板提供:沪江博客