1. 程式人生 > >JasperReport開發筆記

JasperReport開發筆記

  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));

       }
}