论坛首页 Java企业应用论坛

使用jasperReport实现动态表头

浏览 19573 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-12-09  

       最近在我公司drp(运营分销系统)开发中,需要大量报表,由于本人有过jasperReport的开发经验,所以选用了它,jr确实不错,开源,可扩展性很好,缺点就是免费的文档很少,更可气的是,代码中的doc少的可怜,基本上没有参考价值.

       由于我们的产品是用于服装行业的,在服装行业有一个尺码组,非常的麻烦,在制作含有尺码组的报表时,表头的各种尺码不能写死,要从数据库查询出来.但是,一般的报表工具都是不支持表头动态化的(我理解,报表嘛,是呈现给特定人物如老板看的特定内容,表头应该是设计好的,不会经常性的更改),jasperReport也是一样,并不直接支持,细究它的实现过程,我们还是可以扩展从而解决这个问题的.

      先看jasperReport的流程图.

           从上图可看到,jrxml文件要通过JRXmlLoader解析为一个JasperDesign的对象,从源码中可以看出,此对象用java类去描述了报表的整个设计,比如,columnHeader,detail,columnFooter等等.然后由JasperCompileManager编译为一个JasperReport对象,其实,如果你用ireport(jasperReport报表的可视化设计器)制作报表,你完全可以不必理会怎样生成jaserReport对象.ireport对此有很好的支持.

       了解了以上过程,我们可以看出,如果要动态的加入设计元素,只能在JasperDesign对象中下手.加入需要的动态元素,我的需求是在columnHeader中加入一个尺码组的表头,代码实现如下.

java 代码
  1. package com.webstone.drp.report.common.dynamicHeader;   
  2.   
  3. import java.io.File;   
  4. import java.lang.reflect.InvocationTargetException;   
  5. import java.util.Iterator;   
  6. import org.apache.commons.beanutils.BeanUtils;   
  7. import net.sf.jasperreports.engine.JRException;   
  8. import net.sf.jasperreports.engine.JasperCompileManager;   
  9. import net.sf.jasperreports.engine.JasperReport;   
  10. import net.sf.jasperreports.engine.design.JRDesignBand;   
  11. import net.sf.jasperreports.engine.design.JRDesignStaticText;   
  12. import net.sf.jasperreports.engine.design.JasperDesign;   
  13. import net.sf.jasperreports.engine.xml.JRXmlLoader;   
  14.   
  15. /**  
  16.  * @author yaer  
  17.  */  
  18. @SuppressWarnings("unchecked")   
  19. public class ReportDesignProcess {   
  20.     private static final String flagTextKey = "customFlagText";   
  21.   
  22.     public static JasperReport getJasperReport(String xmlFilePath,   
  23.             String[][] sizeGroup) throws JRException {   
  24.         JasperDesign design = getJasperDesign(xmlFilePath);   
  25.         JRDesignBand columnHeader = (JRDesignBand) design.getColumnHeader();   
  26.   
  27.         reSetColumnHeaderHeight(columnHeader, sizeGroup);   
  28.         reSetshapeAndPosition(columnHeader, sizeGroup);   
  29.         addElementToColumnHeader(columnHeader, sizeGroup);   
  30.         return JasperCompileManager.compileReport(design);   
  31.     }   
  32.     private static JasperDesign getJasperDesign(String filePath)   
  33.             throws JRException {   
  34.         return JRXmlLoader.load(new File(filePath));   
  35.     }   
  36.     private static void reSetColumnHeaderHeight(JRDesignBand columnHeader,   
  37.             String[][] sizeGroup) {   
  38.         columnHeader.setHeight(columnHeader.getHeight() * sizeGroup.length);   
  39.     }   
  40.     private static JRDesignStaticText getFlagTextInDesign(   
  41.             JRDesignBand columnHeader) {   
  42.         return (JRDesignStaticText) columnHeader.getElementByKey(flagTextKey);   
  43.     }   
  44.     private static void reSetshapeAndPosition(JRDesignBand columnHeader,   
  45.             String[][] sizeGroup) {   
  46.         JRDesignStaticText flagText = getFlagTextInDesign(columnHeader);   
  47.         Iterator<jrdesignstatictext></jrdesignstatictext> children = columnHeader.getChildren()   
  48.                 .iterator();   
  49.         JRDesignStaticText element;   
  50.         while (children.hasNext()) {   
  51.             element = children.next();   
  52.             if (element.getX() > flagText.getX()) {   
  53.                 element.setX(flagText.getX() + flagText.getWidth()   
  54.                         * sizeGroup[0].length);   
  55.             }   
  56.             if (!flagTextKey.equals(element.getKey())) {   
  57.                 element.setHeight(element.getHeight() * sizeGroup.length);   
  58.             }   
  59.         }   
  60.     }   
  61.     private static void addElementToColumnHeader(JRDesignBand columnHeader,   
  62.             String[][] sizeGroup) {   
  63.         JRDesignStaticText flagText = getFlagTextInDesign(columnHeader);   
  64.         columnHeader.removeElement(flagText);   
  65.         for (int i = 0; i < sizeGroup.length; i++) {   
  66.             for (int j = 0; j < sizeGroup[i].length; j++) {   
  67.                 try {   
  68.                     JRDesignStaticText newElement = (JRDesignStaticText) BeanUtils   
  69.                             .cloneBean(flagText);   
  70.                     newElement.setText(sizeGroup[i][j]);   
  71.                     newElement.setX(flagText.getX() + flagText.getWidth() * j);   
  72.                     newElement.setY(flagText.getY() + flagText.getHeight() * i);   
  73.                     columnHeader.addElement(newElement);   
  74.                 } catch (IllegalAccessException e) {   
  75.                     e.printStackTrace();   
  76.                 } catch (InstantiationException e) {   
  77.                     e.printStackTrace();   
  78.                 } catch (InvocationTargetException e) {   
  79.                     e.printStackTrace();   
  80.                 } catch (NoSuchMethodException e) {   
  81.                     e.printStackTrace();   
  82.                 }   
  83.             }   
  84.         }   
  85.     }   
  86. }  

      很遗憾,没有写注解,原因是我看了一本书叫<<测试驱动开发>>,里面有一句话"意图导向编程",意思是说,用手段比如容易理解,贴切的类名,方法名,属性达到让读者轻易理解代码.从而少写注解,让代码更简捷.如果大家不大明白以上代码的意思,那就是我写的不够好,还要继续努力.

  此类只有一个方法,根据传来的报表文件路径和一个二维数组式的尺码组生成一个jaserReport的对象.有三个关键方法.重新设置columnHeader的height;重新设置静态内容的形状和大小,添加新的元素到columnHeader中,其实,这儿有一个不太容易理的东西:类中有一个flagTextKey的属性,它是标识报表设计中动态内容的一个样板元素,为什么要这个样板元素了,因为用它承载动态内容的样式,要比在用代码实现方便的多.请看BeanUtils.coloneBean()方法,实际上是克隆样板元素对象.

        这个类设计的太具体于应用,应该写成一个抽象方法,让子类来具体实现加入动态元素的过程,我相信大家的需求和我不太一样.由于时间关系,我没有仔细考究.毕竟这只是一个参考实现.

  最后,在用于ireport画报表时就要注意了,一呈不变的元素该怎么画就怎么画,但样板元素的位置一定要放好.动态内容起始的位置和样式就靠它来定义,大多数时候,它是一个标签.只不过它的"key"属性和上面类的"flagTextKey"要保持一致.

  这个话题就到这儿了,我这儿还有一个我包装的工具类,我们公司的同事都认为对开发报表有帮助.

java 代码
  1. package com.webstone.drp.report.common;   
  2.   
  3. import java.util.ArrayList;   
  4. import java.util.Collection;   
  5. import java.util.HashMap;   
  6. import java.util.Map;   
  7. import javax.servlet.http.HttpServletRequest;   
  8. import javax.servlet.http.HttpSession;   
  9. import javax.faces.context.FacesContext;   
  10. import org.apache.commons.lang.ArrayUtils;   
  11. import com.webstone.drp.report.common.dataSource.JRArrayCollectionDataSource;   
  12. import com.webstone.drp.report.common.dynamicHeader.ReportDesignProcess;   
  13. import net.sf.jasperreports.engine.JRException;   
  14. import net.sf.jasperreports.engine.JasperFillManager;   
  15. import net.sf.jasperreports.engine.JasperPrint;   
  16. import net.sf.jasperreports.engine.JasperReport;   
  17. import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;   
  18. import net.sf.jasperreports.engine.JRDataSource;   
  19. import net.sf.jasperreports.engine.util.JRLoader;   
  20.   
  21. /**  
  22.  * 使用jasperReport做报表时的工具支持类.有两个用途,生成jasperPrint对象,和设置导出时的session  
  23.  *   
  24.  * @author yaer  
  25.  * @date 2006-8-26   
  26.  * @modify date 2006-12-8  
  27.  */  
  28. public class ReportUtils {   
  29.     public static final String XLS = "xls";// 导出为xls文件;   
  30.     public static final String PDF = "pdf";// 导出为pdf文件;   
  31.     private static final String JASPER = "jasper";// 编译过后的报表文件;   
  32.     private static final String JRXML = "jrxml";// 原始的报表文件(xml格式);   
  33.     private HttpServletRequest request;   
  34.     private HttpSession session;   
  35.     private String rootPath;// 报表文件路径   
  36.   
  37.     /**  
  38.      * 在jsf环境下时构造些工具类对象  
  39.      *   
  40.      * @param context  
  41.      *            jsf的上下文对象  
  42.      */  
  43.   
  44.     public ReportUtils(FacesContext context) {   
  45.         request = (HttpServletRequest) context.getExternalContext()   
  46.                 .getRequest();   
  47.         session = (HttpSession) context.getExternalContext().getSession(true);   
  48.         this.createRootPath(request);// 生成报表文件的绝对路径   
  49.     }   
  50.     /**  
  51.      * 在其它web环境下构造此工具类对象  
  52.      *   
  53.      * @param req  
  54.      *            request请求对象  
  55.      */  
  56.     public ReportUtils(HttpServletRequest req) {   
  57.         this.request = req;   
  58.         this.session = req.getSession();   
  59.         this.createRootPath(request);// 生成报表文件的绝对路径   
  60.     }   
  61.     /**  
  62.      * 获得报表文件的绝对路径  
  63.      *   
  64.      * @return rootPath  
  65.      */  
  66.     public String getRootPath() {   
  67.         return rootPath;   
  68.     }   
  69.     /**  
  70.      * 获得JasperPrint对象; jasperPrint对象在jasperReport中是填充了报表数据后的一个实体,打印,导出,显示都要使用它.  
  71.      * 此方法含有java5.0支持的'可变参数'特性.params其实质是一个对象数组.在调用些方法时要注意它可能的参数顺序.  
  72.      * 此方法参数描述:  
  73.      * 1、最多只有四个参数。  
  74.      * 2、固定参数filePath表示报表文件的路径,为了支持drp系统中动态尺码组做表头的特性, filePath包括两类:  
  75.      *    编译过后的文件扩展名为'.jasper'和未编译的原始xml文件'.jrxml';  
  76.      *    若报表中有动态尺码组作表头,则filePath为扩展名是'.jrxml'的文件。  
  77.      *    若报表中不涉及动态尺码组,则filePath为扩展名是'.jasper'的文件。  
  78.      * 3、可变参数params的完整列表是(注意顺序):Object obj/Collection dataSource,String seprator,String[][] sizeGroup.  
  79.      *    这三个参数中,有一个例外,Object obj/Collection dataSource必须有一个,此参数表示填充报表的数据,可以是一个Collection式的集合,  
  80.      *    也可以是一个model对象(有且只有一个Collection的属性);  
  81.      *    String seprator表示分隔符,如果数据源是一个Array的集合,则需此参数。String[][]sizeGroup表款尺码组的二维数组。  
  82.      *   
  83.      * @param filePath  
  84.      * @param params  
  85.      * @return jasperPrint  
  86.      */  
  87.     public JasperPrint getJasperPrint(String filePath, Object... params) {   
  88.         JasperReport jasperReport = null;   
  89.         try {   
  90.             if (JASPER.equals(filePath.substring(filePath.indexOf(".") + 1,   
  91.                     filePath.length()))) {// jasper式文件的处理   
  92.                 jasperReport = getReportTemplate(filePath);   
  93.             }   
  94.             if (JRXML.equals(filePath.substring(filePath.indexOf(".") + 1,   
  95.                     filePath.length()))) {// jrxml式文件的处理   
  96.                 jasperReport = ReportDesignProcess.getJasperReport(filePath,   
  97.                         (String[][]) params[params.length - 1]);// 重新设置表头,编译   
  98.                 params = ArrayUtils.remove(params, params.length - 1);// 删除参数中的sizeGroup   
  99.             }   
  100.             return fillReport(jasperReport, params);   
  101.         } catch (JRException e) {   
  102.             e.printStackTrace();   
  103.         }   
  104.         return null;   
  105.     }   
  106.     /**  
  107.      * 获得JasperPrint对象;自定义填充报表时的parameter和dataSource. 参数说明和动态表头的用法参考上一方法  
  108.      * @param filePath  
  109.      * @param parameter  
  110.      * @param dataSource  
  111.      * @param sizeGroup  
  112.      * @return  
  113.      */  
  114.     public JasperPrint getJasperPrint(String filePath, Map parameter,   
  115.             JRDataSource dataSource, Object... sizeGroup) {   
  116.         JasperReport jasperReport = null;   
  117.         try {   
  118.             if (sizeGroup.length == 0) {   
  119.                 jasperReport = getReportTemplate(filePath);   
  120.             }   
  121.             if (sizeGroup.length == 1) {   
  122.                 jasperReport = ReportDesignProcess.getJasperReport(filePath,   
  123.                         (String[][]) sizeGroup[sizeGroup.length - 1]);// 重新设置表头,编译   
  124.             }   
  125.             return JasperFillManager.fillReport(jasperReport, parameter,   
  126.                     dataSource);   
  127.         } catch (JRException e) {   
  128.             e.printStackTrace();   
  129.         }   
  130.         return null;   
  131.     }   
  132.     public void setAttrToPage(JasperPrint jasperPrint, String report_fileName,   
  133.             String report_type) {   
  134.         session.setAttribute("REPORT_JASPERPRINT", jasperPrint);   
  135.         session.setAttribute("REPORT_FILENAME", report_fileName);   
  136.         session.setAttribute("REPORT_TYPE", report_type);   
  137.     }   
  138.     private JasperPrint fillReport(JasperReport jasperReport, Object[] params)   
  139.             throws JRException {   
  140.         Map parameters = null;   
  141.         JRDataSource ds = null;   
  142.         if (params.length == 0) {   
  143.             return null;   
  144.         }   
  145.         if (params.length == 1 && params[0].getClass() == ArrayList.class) {//其实质是要判断是否是集合   
  146.             ds = new JRBeanCollectionDataSource((Collection) params[0]);   
  147.         }   
  148.         if (params.length == 1 && params[0].getClass() != ArrayList.class) {   
  149.             ClassAnalysis ca = new ClassAnalysis(params[0]);   
  150.             parameters = ca.getFields();   
  151.             ds = new JRBeanCollectionDataSource(ca.getSet());   
  152.         }   
  153.         if (params.length == 2 && params[0].getClass() == ArrayList.class) {   
  154.             ds = new JRArrayCollectionDataSource((Collection) params[0],   
  155.                     params[1].toString());   
  156.         }   
  157.         if (params.length == 2 && params[0].getClass() != ArrayList.class) {   
  158.             ClassAnalysis ca = new ClassAnalysis(params[0]);   
  159.             parameters = ca.getFields();   
  160.             ds = new JRArrayCollectionDataSource(ca.getSet(), params[1]   
  161.                     .toString());   
  162.         }   
  163.         return JasperFillManager.fillReport(jasperReport,   
  164.                 parameters == null ? new HashMap() : parameters, ds);   
  165.     }   
  166.     private void createRootPath(HttpServletRequest request) {   
  167.         rootPath = request.getSession().getServletContext().getRealPath("/")   
  168.                 + "WEB-INF\\classes\\com\\webstone\\drp\\report\\jaser\\";   
  169.     }   
  170.     private JasperReport getReportTemplate(String jasperPath)   
  171.             throws JRException {   
  172.         return (JasperReport) JRLoader.loadObject(rootPath + jasperPath);   
  173.     }   
  174. }   
   此类用于生成填充后报表对象jasperPrint.
   发表时间:2006-12-11  
看了深受启示,我们公司最近也在用这个来开发报表,可以做到动态生成报表的模板,可是里面的打印问题一直没有解决,你是如何做到在客户端上进行打印的呢?

如果直接调用以下代码的话:

JasperPrintManager.printReport(path+"pages/reportmodel/"+userCreatpath+"/user.jrprint", true);

会调用服务器上的打印机进行打印,而如果在客户端上访问服务器上的页面的话,也只会调用服务器上打印机去打印,而不会调用客户机的打印机进行打印,不知道你是怎么处理这些问题的?谢谢!
0 请登录后投票
   发表时间:2007-03-29  
能根据查询出的 list 动态的生成一张报表吗?

非常感谢!
0 请登录后投票
   发表时间:2007-03-29  
yxxcrtd 写道
能根据查询出的 list 动态的生成一张报表吗?

非常感谢!
用poi可以做到使用list动态生成报表,包括动态生成表头,哪天有空我弄上来玩玩.
0 请登录后投票
   发表时间:2007-03-29  
dovecat 写道
yxxcrtd 写道
能根据查询出的 list 动态的生成一张报表吗?

非常感谢!
用poi可以做到使用list动态生成报表,包括动态生成表头,哪天有空我弄上来玩玩.
Really ?!

赶紧吧,可找到救星了!!!
0 请登录后投票
   发表时间:2007-03-29  
yxxcrtd 写道
dovecat 写道
yxxcrtd 写道
能根据查询出的 list 动态的生成一张报表吗?

非常感谢!
用poi可以做到使用list动态生成报表,包括动态生成表头,哪天有空我弄上来玩玩.
Really ?!

赶紧吧,可找到救星了!!!
最好写的详细一点儿啊! 非常感谢非常感谢了!!!
0 请登录后投票
   发表时间:2007-04-02  
yxxcrtd 写道
yxxcrtd 写道
dovecat 写道
yxxcrtd 写道
能根据查询出的 list 动态的生成一张报表吗?

非常感谢!
用poi可以做到使用list动态生成报表,包括动态生成表头,哪天有空我弄上来玩玩.
Really ?!

赶紧吧,可找到救星了!!!
最好写的详细一点儿啊! 非常感谢非常感谢了!!!

http://www.iteye.com/topic/67324
0 请登录后投票
   发表时间:2007-04-02  
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)
............
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics