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很容易解决
.....
- 用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方法
- .jrxml vs .jasper
- 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);
- 不要有空白地方! 首先把所有的field设成一样高, 对齐! 把所在band的高度也设成和field一样高, 让field正好放入band. 然后调整field的宽度, 让每个field都相邻,没有空隙. 最后,记得设置参数: exporter.setparameter(jrxlsexporterparameter.is_remove_empty_space_between_rows,
- 如何保留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);
- 因为第一行和第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
- 如何去掉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);
- java.lang.reflect.field pageheight = jrbasereport.class.getdeclaredfield(
- 如前说述, 由于page break的关系, excel中每隔几十行,就有一个高度为0的row, 即使把page botom margin设为0, 把page footer去掉都没有办法. 唯一的解决办法是把page height设为很大. 同5.5一样, 不得不使用reflect:
- 如何去掉报表头等
- 文档
- 哪里有文档
- 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));
}
}
闽公网安备 35060202000074号