jasperreports是个很好用的报表引擎,近期研究了一下与struts2的接合应用问题。目前从struts2网站上下载的插件支持的数据源很单一,只能够支持List<实体类>的数据形式,这样就对程序员开发报表的灵活性作了很大的限制。jasperreports支持的数据源有很多种,例如List<Map>形式的JRMapCollectionDataSource,或者与Hibernate配合应用的JRHibernateListDataSource等等,目前struts2中的插件即struts2-jasperreports-plugin让人很不爽,用了几分钟时间改造一下它,让它支持所有JasperReports能够使用的数据源。
修改过程如下:
1.使用svn工具从struts2的代码仓库中获得struts2-jasperreports-plugin的源代码,打开JasperReportsResult.java文件,找到如下这段代码:
// Handle IE special case: it sends a "contype" request first.
// TODO Set content type to config settings?
if ("contype".equals(request.getHeader("User-Agent"))) {
try {
response.setContentType("application/pdf");
response.setContentLength(0);
ServletOutputStream outputStream = response.getOutputStream();
outputStream.close();
} catch (IOException e) {
LOG.error("Error writing report output", e);
throw new ServletException(e.getMessage(), e);
}
return;
}
// Construct the data source for the report.
ValueStack stack = invocation.getStack();
在这些代码以后进行修改,定义一个JRDataSource接口,然后判断action当中提供的数据源是否满足JRDataSource接口,修改的代码如下:
// Construct the data source for the report.
ValueStack stack = invocation.getStack();
Object dataSourceObject=stack.findValue(dataSource);
JRDataSource jrDataSource=null;
//以下判断是否满足JRDataSource接口
try{
jrDataSource=(JRDataSource)dataSourceObject;
}catch(Exception e){
jrDataSource = new ValueStackDataSource(stack, dataSource);
}
最后再修改一下生成报表时填充数据的参数,如下:
jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, jrDataSource);
这样就可以了。
另外这个struts2-jasperreports-plugin的代码好像和我对jasperreports的理解有点问题,居然提取字段数据时先按描述来进行提取的,我在使用其它的报表工具时都是先按fieldname去提取数据,所以顺便将ValueStackDataSource中的代码代码改一下,如下:
public Object getFieldValue(JRField field) throws JRException {
//TODO: move the code to return a ValueStackDataSource to a seperate
// method when and if the JRDataSource interface is updated to support
// this.
String expression = field.getName();
if (expression == null) {
//Description is optional so use the field name as a default
expression = field.getDescription();
}
编译时还有一些小小的问题,log类找不到,随便改成Log4j的或者什么的都可以,经测试很爽。
例如与Spring当中的JdbcTemplate接合在一起应用时:
String query_sql="select b.name,a.class_name,a.edu_start_year,d.student_name from class_info a " +
"inner join teacher b on a.teacher_id=b.teacher_id inner join student_class c " +
"on a.class_info_code=c.class_info_code inner join student d " +
"on c.student_id=d.student_id where a.class_info_code=? order by b.name,a.class_name";
List<Map> tmp_dataList=this.jdbcTemplate.queryForList(query_sql,new Object[]{Integer.valueOf(this.class_info_code)},new int[]{Types.INTEGER});
this.datalist = new JRMapCollectionDataSource(tmp_dataList);
注意这里向struts2-jasperreports-plugin提供数据的是datalist对象,数据类型是JRMapCollectionDataSource,在struts2的配置参数中指定dataSource为datalist就行啦!