JasperReport经验谈

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

iReport
安装
下载,解压iReport 0.4.0 (推荐src版本)
确认JDK是1.4以上
把JDK /lib下的tools.jar拷贝到{ireport_home}/lib目录中
运行
对于下载的Binary版本,只能运行/bin/startup.bat
对于下载的Src版本,可以通过ant iReport运行(先安装ant)
如果运行startup.bat,出现java.lang.NoSuchMethodError错误,一般是JDK版本太低。如果确认已安装了1.4或以上,检查path系统变量,看看有1.3的JRE是不是排在前面(比如安装了Oracle的客户端,往往有1.3的JRE),如果出现Class Not Found,检查classpath。对于通过ant的方式运行,一般都没什么问题,所以推荐下载src版本
JasperReport 常见问题
.jrxml vs .jasper
如果在运行时载入.jrxml, 那么每次调用还得编译, 不如预先编译成.jasper.不过预先编译的jasper,必须用同样版本的JasperReport载入,而且灵活性差些. 不过对于大部分报表,还是预先编译成jasper方便
如果批量编译jrxml
用Ant很容易解决

.....

如何使用图片?
很容易,用Image控件就可以了. 在Image Express里面可以用String来表示图片的路径, 或者用InputStream, File对象.不过不管用File还是String对象, 都不得不用绝对路径, 这显然很不灵活. 解决办法是,穿入一个$P的参数,表示图片所在的目录,然后用$P和文件名拼接出完整的绝对路径. 更好的方法是用InputStream, 例如this.getClass().getResourceAsStream("logo.jpg") ,这时只要把图片放在当前.jasper所在的目录就可以了,不必考虑什么参数,什么路径了
显示非数据库字段变量
显示如运行日期等,可以直接在Text Field里面输入new java.util.Date(), 然后把Pattern设成如mm/dd/yyyy.
动态控制某些Field是否显示
每个Static Text, Text Field甚至整个Band的属性里面都有Print When Expression, 比如设成new Boolean(!$P{isDisplay}.equalsIgnoreCase("yes")), 那么只有当参数display的值为yes的时候才显示
使用Sub Report, 如何使用相对路径
见1.3, 和使用图片类似, 用InputStream或者传入参数
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''
如何使用图表(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, ....)
如果显示多个图表
在一张报表上显示一个图表和显示多个图表是不同的. 假设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方法
Export到Excel的问题
如何去掉报表头等
直接把不需要的Band删除(把其高度设为0). 如果仅仅是export到Excel的时候不需要报表头, 而输出到PDF等仍然需要保留, 那么使用print when expression, 见4.4
如果让Excel看起来整齐
不要有空白地方! 首先把所有的Field设成一样高, 对齐! 把所在Band的高度也设成和Field一样高, 让Field正好放入Band. 然后调整Field的宽度, 让每个Field都相邻,没有空隙. 最后,记得设置参数: exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,
Boolean.TRUE);

如何保留GridLine
首先, 设置参数exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE); 然后,把每个Field或者Static Text框的''Transparent''属性都勾上
如何使字段名只显示一次
如果把字段名放在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的时候才显示
为什么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);
如何去掉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);

文档
哪里有文档
JasperReport有份Ultimate Guide, 不过不是免费的, 和jFreeChart一个德行. 不过网上有流传, 写的还可以, 60多页, 不过也没详细到哪里去. 如果下载源代码版, 那么看看自带的Demo也不错. SF的论坛也是问问题的最好地方
源代码 仅供参考(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));

}
}

时间: 2024-10-14 20:12:16

JasperReport经验谈的相关文章

王通:网络出版赚钱经验谈

网络|赚钱 王通:网络出版赚钱经验谈用传统出版社,我发行了一本印刷版书,从写书到发行,用了一年的心血,仅仅收到了几千元稿费. l 利用互联网发行,我发行的第一本电子书,从写书到发行,用了不到一个月,两年下来,这本书已经给我增加了百万收入. 同样是发行?效率和收入的差距如此的大,可以看出网络出版发行的魅力了. 王朔最近打算抛弃出版社了,欲通过网络发行自己的小说赚钱,其他作家可能也都打算来尝试.尝试意味着是摸着石头过河,操作起来有可能因缺乏经验而走很多弯路.王通在此分享一下自己在这方面相对比较成熟的

网站推广经验谈

推广|网站推广 网站推广经验谈  一个网站无论做得如何完美,如果不将它推广开来,靠推广来提高它的访问量,那么,这个网站只能说是做给自己看的,它存在的意义就大为逊色了.可如何推广,如何提升自己网站的访问量,并且能够吸引客户的眼球,让他从今天的新客户变成明天的老客户?结合笔者自身经验,今天就从内部因素和外部因素两个方面来聊聊这个问题. 一.内部因素    首先,得有一个内容.版面.规划.设计都差不多的网页,这些东西作为你网站的门面,一定要注意主页的美工设计是否到位,整体的框架结构是否合理,页面上所用

IReport与JasperReport开发详解二

详解  IReport与JasperReport开发详解二        3.2.1  设计报表                现在请点击菜单"DataSource"然后选取中"Report query"项,也可以点击图标 ,接着IReport会弹出一个对话框(如图):现在我们在"Report SQL Query"中输入SQL语句"select * from titles",然后去掉"Automatically Re

jasperreport中的demo学习

首先需要安装ant,注意一点就是不要忘记了在path中设置path,比如,我系统中安装的ant是在D:\apache-ant-1.6.2,那么就要在path中设置该环境变量.接下来就是下载jasperreport了,下载那个带有demo的,将他解压缩,至此前期工作就完成了. 打开\jasperreport\demo amples,在这里就是官方提供的所有的例子: 1.  Alterdesign 用ant运行这个例子展现了jasperreport的整体工作流程,同时展现了pdf文件如何生成,背景颜

IReport与JasperReport开发详解一

详解  IReport与JasperReport开发详解一1.简介:        Jasperreport是一个报表制作工具,用户须按照它编写的规则编写一个XML,然后得到用户所要的报表文件. Jasperreport程序库是一个灵活,功能强大的报表产生工具,可以以PDF, HTML或XML等多种形式产生报表, 并支持CSV, XLS,等格式报表.该引擎由java编写,支持多种形式应用程序产生动态报表.打印的报表文档,Jasperreport是按照一个预定义的xml文档来组织报表的数据, 这些

数据库设计经验谈 一 (引)

设计|数据|数据库|数据库设计  数据库设计经验谈 作者: 水若寒 一个成功的管理系统,是由:[50% 的业务 + 50% 的软件] 所组成,而 50% 的成功软件又有 [25% 的数据库 + 25% 的程序] 所组成,数据库设计的好坏是一个关键.如果把企业的数据比做生命所必需的血液,那么数据库的设计就是应用中最重要的一部分.有关数据库设计的材料汗牛充栋,大学学位课程里也有专门的讲述.不过,就如我们反复强调的那样,再好的老师也比不过经验的教诲.插入一些数据库设计心得: 一. 设计思想对许多程序员

路由表信息经验谈

当前的路由: destination 目的网段 mask 子网掩码 interface 到达该目的地的本路由器的出口ip gateway 下一跳路由器入口的ip,路由器通过interface和gateway定义一调到下一个路由器的链路,通常情况下,interface和gateway是同一网段的metric 跳数,该条路由记录的质量,一般情况下,如果有多条到达相同目的地的路由记录,路由器会采用metric值小的那条路由. 源码: Active Routes: Network Destination

Eclipse RCP中使用JasperReport制作报表

Eclipse RCP用来开发Java客户端应用非常爽,现在它本身也有BIRT报表项目 支持Eclipse RCP中使用报表.不过四年前我开始开发时,却没这么幸运,于是怎 么在RCP中生成报表成我一个头痛的事情. 琢磨了几天,终于让我想出一个在我的Eclipse RCP程序中使用JasperReport 报表的方法: 1.报表模版存放在服务端特定的一个资源目录下. 2.客户端获得客户输入的报表参数(查询范围.分组条件)及对应报表模版 名称(与服务端有命名约定) 3.客户端提交request对象至

JasperReport那些事儿(六)——制作分页式报表

之前介绍的一些报表都是单页式的报表,就是格式都一样.如果要生成一份有不同样式的报表,这要怎么做呢?就像一份保险公司保单,可能有产品介绍.收益表.建议书.最后还有合同,这都要求不同的报表分页显示.这一篇将给你介绍怎么让报表分页. 先来看一下效果. 这份报表沿用了上一篇的报表,只是在这个基础上加了分页功能. 实际上我只改动了DepartmentList_department.jrxml这张报表.就在detail区域top=0处加入一个分页符(page break),因为分页符需要占用1像素的高度,所