- yueho
- 等级:
- 性别:
- 文章: 6
- 积分: 243
|
最近在我公司drp(运营分销系统)开发中,需要大量报表,由于本人有过jasperReport的开发经验,所以选用了它,jr确实不错,开源,可扩展性很好,缺点就是免费的文档很少,更可气的是,代码中的doc少的可怜,基本上没有参考价值.
由于我们的产品是用于服装行业的,在服装行业有一个尺码组,非常的麻烦,在制作含有尺码组的报表时,表头的各种尺码不能写死,要从数据库查询出来.但是,一般的报表工具都是不支持表头动态化的(我理解,报表嘛,是呈现给特定人物如老板看的特定内容,表头应该是设计好的,不会经常性的更改),jasperReport也是一样,并不直接支持,细究它的实现过程,我们还是可以扩展从而解决这个问题的.
先看jasperReport的流程图.
从上图可看到,jrxml文件要通过JRXmlLoader解析为一个JasperDesign的对象,从源码中可以看出,此对象用java类去描述了报表的整个设计,比如,columnHeader,detail,columnFooter等等.然后由JasperCompileManager编译为一个JasperReport对象,其实,如果你用ireport(jasperReport报表的可视化设计器)制作报表,你完全可以不必理会怎样生成jaserReport对象.ireport对此有很好的支持.
了解了以上过程,我们可以看出,如果要动态的加入设计元素,只能在JasperDesign对象中下手.加入需要的动态元素,我的需求是在columnHeader中加入一个尺码组的表头,代码实现如下.
java 代码
- package com.webstone.drp.report.common.dynamicHeader;
-
- import java.io.File;
- import java.lang.reflect.InvocationTargetException;
- import java.util.Iterator;
- import org.apache.commons.beanutils.BeanUtils;
- import net.sf.jasperreports.engine.JRException;
- import net.sf.jasperreports.engine.JasperCompileManager;
- import net.sf.jasperreports.engine.JasperReport;
- import net.sf.jasperreports.engine.design.JRDesignBand;
- import net.sf.jasperreports.engine.design.JRDesignStaticText;
- import net.sf.jasperreports.engine.design.JasperDesign;
- import net.sf.jasperreports.engine.xml.JRXmlLoader;
-
-
-
-
- @SuppressWarnings("unchecked")
- public class ReportDesignProcess {
- private static final String flagTextKey = "customFlagText";
-
- public static JasperReport getJasperReport(String xmlFilePath,
- String[][] sizeGroup) throws JRException {
- JasperDesign design = getJasperDesign(xmlFilePath);
- JRDesignBand columnHeader = (JRDesignBand) design.getColumnHeader();
-
- reSetColumnHeaderHeight(columnHeader, sizeGroup);
- reSetshapeAndPosition(columnHeader, sizeGroup);
- addElementToColumnHeader(columnHeader, sizeGroup);
- return JasperCompileManager.compileReport(design);
- }
- private static JasperDesign getJasperDesign(String filePath)
- throws JRException {
- return JRXmlLoader.load(new File(filePath));
- }
- private static void reSetColumnHeaderHeight(JRDesignBand columnHeader,
- String[][] sizeGroup) {
- columnHeader.setHeight(columnHeader.getHeight() * sizeGroup.length);
- }
- private static JRDesignStaticText getFlagTextInDesign(
- JRDesignBand columnHeader) {
- return (JRDesignStaticText) columnHeader.getElementByKey(flagTextKey);
- }
- private static void reSetshapeAndPosition(JRDesignBand columnHeader,
- String[][] sizeGroup) {
- JRDesignStaticText flagText = getFlagTextInDesign(columnHeader);
- Iterator<jrdesignstatictext></jrdesignstatictext> children = columnHeader.getChildren()
- .iterator();
- JRDesignStaticText element;
- while (children.hasNext()) {
- element = children.next();
- if (element.getX() > flagText.getX()) {
- element.setX(flagText.getX() + flagText.getWidth()
- * sizeGroup[0].length);
- }
- if (!flagTextKey.equals(element.getKey())) {
- element.setHeight(element.getHeight() * sizeGroup.length);
- }
- }
- }
- private static void addElementToColumnHeader(JRDesignBand columnHeader,
- String[][] sizeGroup) {
- JRDesignStaticText flagText = getFlagTextInDesign(columnHeader);
- columnHeader.removeElement(flagText);
- for (int i = 0; i < sizeGroup.length; i++) {
- for (int j = 0; j < sizeGroup[i].length; j++) {
- try {
- JRDesignStaticText newElement = (JRDesignStaticText) BeanUtils
- .cloneBean(flagText);
- newElement.setText(sizeGroup[i][j]);
- newElement.setX(flagText.getX() + flagText.getWidth() * j);
- newElement.setY(flagText.getY() + flagText.getHeight() * i);
- columnHeader.addElement(newElement);
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
很遗憾,没有写注解,原因是我看了一本书叫<<测试驱动开发>>,里面有一句话"意图导向编程",意思是说,用手段比如容易理解,贴切的类名,方法名,属性达到让读者轻易理解代码.从而少写注解,让代码更简捷.如果大家不大明白以上代码的意思,那就是我写的不够好,还要继续努力.
此类只有一个方法,根据传来的报表文件路径和一个二维数组式的尺码组生成一个jaserReport的对象.有三个关键方法.重新设置columnHeader的height;重新设置静态内容的形状和大小,添加新的元素到columnHeader中,其实,这儿有一个不太容易理的东西:类中有一个flagTextKey的属性,它是标识报表设计中动态内容的一个样板元素,为什么要这个样板元素了,因为用它承载动态内容的样式,要比在用代码实现方便的多.请看BeanUtils.coloneBean()方法,实际上是克隆样板元素对象.
这个类设计的太具体于应用,应该写成一个抽象方法,让子类来具体实现加入动态元素的过程,我相信大家的需求和我不太一样.由于时间关系,我没有仔细考究.毕竟这只是一个参考实现.
最后,在用于ireport画报表时就要注意了,一呈不变的元素该怎么画就怎么画,但样板元素的位置一定要放好.动态内容起始的位置和样式就靠它来定义,大多数时候,它是一个标签.只不过它的"key"属性和上面类的"flagTextKey"要保持一致.
这个话题就到这儿了,我这儿还有一个我包装的工具类,我们公司的同事都认为对开发报表有帮助.
java 代码
- package com.webstone.drp.report.common;
-
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.Map;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpSession;
- import javax.faces.context.FacesContext;
- import org.apache.commons.lang.ArrayUtils;
- import com.webstone.drp.report.common.dataSource.JRArrayCollectionDataSource;
- import com.webstone.drp.report.common.dynamicHeader.ReportDesignProcess;
- import net.sf.jasperreports.engine.JRException;
- import net.sf.jasperreports.engine.JasperFillManager;
- import net.sf.jasperreports.engine.JasperPrint;
- import net.sf.jasperreports.engine.JasperReport;
- import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
- import net.sf.jasperreports.engine.JRDataSource;
- import net.sf.jasperreports.engine.util.JRLoader;
-
-
-
-
-
-
-
-
- public class ReportUtils {
- public static final String XLS = "xls";
- public static final String PDF = "pdf";
- private static final String JASPER = "jasper";
- private static final String JRXML = "jrxml";
- private HttpServletRequest request;
- private HttpSession session;
- private String rootPath;
-
-
-
-
-
-
-
-
- public ReportUtils(FacesContext context) {
- request = (HttpServletRequest) context.getExternalContext()
- .getRequest();
- session = (HttpSession) context.getExternalContext().getSession(true);
- this.createRootPath(request);
- }
-
-
-
-
-
-
- public ReportUtils(HttpServletRequest req) {
- this.request = req;
- this.session = req.getSession();
- this.createRootPath(request);
- }
-
-
-
-
-
- public String getRootPath() {
- return rootPath;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public JasperPrint getJasperPrint(String filePath, Object... params) {
- JasperReport jasperReport = null;
- try {
- if (JASPER.equals(filePath.substring(filePath.indexOf(".") + 1,
- filePath.length()))) {
- jasperReport = getReportTemplate(filePath);
- }
- if (JRXML.equals(filePath.substring(filePath.indexOf(".") + 1,
- filePath.length()))) {
- jasperReport = ReportDesignProcess.getJasperReport(filePath,
- (String[][]) params[params.length - 1]);
- params = ArrayUtils.remove(params, params.length - 1);
- }
- return fillReport(jasperReport, params);
- } catch (JRException e) {
- e.printStackTrace();
- }
- return null;
- }
-
-
-
-
-
-
-
-
- public JasperPrint getJasperPrint(String filePath, Map parameter,
- JRDataSource dataSource, Object... sizeGroup) {
- JasperReport jasperReport = null;
- try {
- if (sizeGroup.length == 0) {
- jasperReport = getReportTemplate(filePath);
- }
- if (sizeGroup.length == 1) {
- jasperReport = ReportDesignProcess.getJasperReport(filePath,
- (String[][]) sizeGroup[sizeGroup.length - 1]);
- }
- return JasperFillManager.fillReport(jasperReport, parameter,
- dataSource);
- } catch (JRException e) {
- e.printStackTrace();
- }
- return null;
- }
- public void setAttrToPage(JasperPrint jasperPrint, String report_fileName,
- String report_type) {
- session.setAttribute("REPORT_JASPERPRINT", jasperPrint);
- session.setAttribute("REPORT_FILENAME", report_fileName);
- session.setAttribute("REPORT_TYPE", report_type);
- }
- private JasperPrint fillReport(JasperReport jasperReport, Object[] params)
- throws JRException {
- Map parameters = null;
- JRDataSource ds = null;
- if (params.length == 0) {
- return null;
- }
- if (params.length == 1 && params[0].getClass() == ArrayList.class) {
- ds = new JRBeanCollectionDataSource((Collection) params[0]);
- }
- if (params.length == 1 && params[0].getClass() != ArrayList.class) {
- ClassAnalysis ca = new ClassAnalysis(params[0]);
- parameters = ca.getFields();
- ds = new JRBeanCollectionDataSource(ca.getSet());
- }
- if (params.length == 2 && params[0].getClass() == ArrayList.class) {
- ds = new JRArrayCollectionDataSource((Collection) params[0],
- params[1].toString());
- }
- if (params.length == 2 && params[0].getClass() != ArrayList.class) {
- ClassAnalysis ca = new ClassAnalysis(params[0]);
- parameters = ca.getFields();
- ds = new JRArrayCollectionDataSource(ca.getSet(), params[1]
- .toString());
- }
- return JasperFillManager.fillReport(jasperReport,
- parameters == null ? new HashMap() : parameters, ds);
- }
- private void createRootPath(HttpServletRequest request) {
- rootPath = request.getSession().getServletContext().getRealPath("/")
- + "WEB-INF\\classes\\com\\webstone\\drp\\report\\jaser\\";
- }
- private JasperReport getReportTemplate(String jasperPath)
- throws JRException {
- return (JasperReport) JRLoader.loadObject(rootPath + jasperPath);
- }
- }
此类用于生成填充后报表对象jasperPrint.
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
返回顶楼 |
|
|
- axiang_2898
- 等级: 初级会员
- 性别:
- 文章: 44
- 积分: 40
- 来自: 上海
|
看了深受启示,我们公司最近也在用这个来开发报表,可以做到动态生成报表的模板,可是里面的打印问题一直没有解决,你是如何做到在客户端上进行打印的呢?
如果直接调用以下代码的话:
JasperPrintManager.printReport(path+"pages/reportmodel/"+userCreatpath+"/user.jrprint", true);
会调用服务器上的打印机进行打印,而如果在客户端上访问服务器上的页面的话,也只会调用服务器上打印机去打印,而不会调用客户机的打印机进行打印,不知道你是怎么处理这些问题的?谢谢!
|
返回顶楼 |
|
|
- yxxcrtd
- 等级: 初级会员
- 文章: 13
- 积分: 40
- 来自: ...
|
能根据查询出的 list 动态的生成一张报表吗?
非常感谢!
|
返回顶楼 |
|
|
- dovecat
- 等级:
- 性别:
- 文章: 663
- 积分: 369
- 来自: 深圳
|
yxxcrtd 写道 能根据查询出的 list 动态的生成一张报表吗?
非常感谢! 用poi可以做到使用list动态生成报表,包括动态生成表头,哪天有空我弄上来玩玩.
|
返回顶楼 |
|
|
- yxxcrtd
- 等级: 初级会员
- 文章: 13
- 积分: 40
- 来自: ...
|
dovecat 写道 yxxcrtd 写道 能根据查询出的 list 动态的生成一张报表吗?
非常感谢! 用poi可以做到使用list动态生成报表,包括动态生成表头,哪天有空我弄上来玩玩. Really ?!
赶紧吧,可找到救星了!!!
|
返回顶楼 |
|
|
- yxxcrtd
- 等级: 初级会员
- 文章: 13
- 积分: 40
- 来自: ...
|
yxxcrtd 写道 dovecat 写道 yxxcrtd 写道 能根据查询出的 list 动态的生成一张报表吗?
非常感谢! 用poi可以做到使用list动态生成报表,包括动态生成表头,哪天有空我弄上来玩玩. Really ?!
赶紧吧,可找到救星了!!! 最好写的详细一点儿啊! 非常感谢非常感谢了!!!
|
返回顶楼 |
|
|
- dovecat
- 等级:
- 性别:
- 文章: 663
- 积分: 369
- 来自: 深圳
|
yxxcrtd 写道 yxxcrtd 写道 dovecat 写道 yxxcrtd 写道 能根据查询出的 list 动态的生成一张报表吗?
非常感谢! 用poi可以做到使用list动态生成报表,包括动态生成表头,哪天有空我弄上来玩玩. Really ?!
赶紧吧,可找到救星了!!! 最好写的详细一点儿啊! 非常感谢非常感谢了!!!
http://www.iteye.com/topic/67324
|
返回顶楼 |
|
|
- dennis_zane
- 等级: 资深会员
- 性别:
- 文章: 1529
- 积分: 2148
- 来自: 杭州
|
yxxcrtd 写道 能根据查询出的 list 动态的生成一张报表吗?
非常感谢!
jasperreport支持JavaBean做数据源的,当然可以做到。简单例子:
public class BeanCollectionDSReportServlet extends HttpServlet
{
private JRDataSource createReportDataSource()
{
JRBeanCollectionDataSource dataSource;
Collection reportRows = initializeBeanCollection();
dataSource = new JRBeanCollectionDataSource(reportRows);
return dataSource;
}
//注意这里,使用ArrayList做数据源
private Collection initializeBeanCollection()
{
ArrayList reportRows = new ArrayList();
reportRows.add(new AircraftData("N263Y", "T-11", "39 ROSCOE TRNR
RACER", "R1830 SERIES"));
reportRows.add(new AircraftData("N4087X", "BA100-163", "BRADLEY
AEROBAT", "R2800 SERIES"));
reportRows.add(new AircraftData("N43JE", "HAYABUSA 1", "NAKAJIMA
KI-43 IIIA", "R1830 SERIES"));
reportRows.add(
new AircraftData("N912S", "9973CC", "PA18-150",
"R-1820 SER"));
return reportRows;
}
protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException
{
ServletOutputStream servletOutputStream = response
getOutputStream();
InputStream reportStream = getServletConfig().getServletContext()
.getResourceAsStream("/reports/BeanDSReport.jasper");
try
{
JRDataSource dataSource = createReportDataSource();
JasperRunManager.runReportToPdfStream(reportStream,
servletOutputStream, new HashMap(), dataSource);
response.setContentType("application/pdf");
servletOutputStream.flush();
servletOutputStream.close();
}
catch (Exception e)
............
|
返回顶楼 |
|
|