Excel作为一种常见的文件格式广泛存在,很多报表的数据来源于Excel。由于报表一般都带有参数,经常需要对原始数据进行分组、过滤等,都需要再编写程序代码才能满足有参数报表的查询需求。

润乾报表使用Excel数据源,需要用Java程序处理文件,通过自定义数据集为报表提供数据源支持。这里通过一个实例说明润乾报表实现过程,以及改进方式。

报表描述

基金公司将股票信息以每月一个Excel存储,命名方式为:stock_yyyyMM.xls。包括每只股票的股票编码、交易日期和收盘价,现报表参数为起始年月和结束年月,报表按照交易日排序,展现股票交易记录列表。

    Excel内容如下:

wKiom1P-x8KAnnyNAACy-tXgYZ0461.jpg

    报表格式如下:

wKiom1P-x8STzvvHAAArGa-bGkw078.jpg

润乾报表实现

自定义数据集

这里使用poi读取操作Excel,以下为实现步骤:

1、  定义股票信息类,用于存储股票交易信息

class Stock {

    //省略构造函数和get set方法

    private String code;

    private String tradingDate;

    private String price;

}

 

2、  自定义数据集中接收并解析报表参数,判断使用哪些文件;

       // 取得参数列表并分别取得它的参数名与值,宏与之类似

       Mapmap = ctx.getParamMap(false);

       int begin = Integer.parseInt(map.get("begin").toString());

       int end = Integer.parseInt(map.get("end").toString());

 

       for (int i = begin; i <= end;i++) {

           System.out.println(i);

           if (i % 100 > 0&& i % 100 < 13) {

              StringfileName = "E:\\stock_" + i + ".xls";

              System.out.println("Read "+ fileName + " startup...");

              readExcel(fileName);//读入并解析Excel内容,并将结果存入List

           }

       }

 

3、  使用getCellValue()方法判断单元格格式,由于Excel会将日期按照numeric类型存储,所以要在程序中区分数值和日期,并完成相应转换

publicstatic StringgetCellValue(Cell cell) {

       switch (cell.getCellType()) { // 根据cell中的类型来输出数据

       case HSSFCell.CELL_TYPE_NUMERIC:

           //解析自定义日期格式yyyy-mm-ddd

           if(cell.getCellStyle().getDataFormat() == 184) {

              SimpleDateFormatsdf = newSimpleDateFormat("yyyy-MM-dd");

              double value =cell.getNumericCellValue();

              Datedate = org.apache.poi.ss.usermodel.DateUtil

                     .getJavaDate(value);

              return sdf.format(date);

           }

           returncell.getNumericCellValue() + "";

       case HSSFCell.CELL_TYPE_STRING:

           returncell.getStringCellValue();

       case HSSFCell.CELL_TYPE_FORMULA:

           returncell.getCellFormula().toString();

       default:

           returnnull;

       }

    }

 

4、实现读入Excel方法readExcel(),根据传入Excel文件名读入并解析,将文件内容存入List

publicstaticvoid readExcel(StringfileName) {

       boolean isE2007 = false; // 判断是否是excel2007格式

       if (fileName.endsWith("xlsx"))

           isE2007= true;

       try {

           InputStreaminput = newFileInputStream(fileName); // 建立输入流

           Workbookwb = null;

           // 根据文件格式(2003或者2007)来初始化

           if (isE2007)

              wb= newXSSFWorkbook(input);

           else

              wb= newHSSFWorkbook(input);

           Sheetsheet = wb.getSheetAt(0); // 获得第一个表单

           Iterator<Row>rows = sheet.rowIterator(); // 获得第一个表单的迭代器

           Stringcode = null;

           Stringdate = null;

           Stringprice = null;

           while (rows.hasNext()) {

              Rowrow = rows.next(); // 获得行数据

              Iterator<Cell>cells = row.cellIterator(); // 获得第一行的迭代器

              while (cells.hasNext()) {

                  Cellcell = cells.next();

                  System.out.println("Cell #"+ cell.getColumnIndex());

                  switch (cell.getColumnIndex()){

                  case 0:

                     code= getCellValue(cell);

                     break;

                  case 1:

                     date= getCellValue(cell);

                     break;

                  case 2:

                     price= getCellValue(cell);

                     break;

                  }

              }

              if (row.getRowNum() == 0){

                  colList.add(new Stock(code, date,price));

              }else{

                  list.add(new Stock(code, date,price));

              }

           }

       }catch(IOException ex) {

           ex.printStackTrace();

       }

    }

 

5、定义排序类,实现compare方法比较交易日期

publicclass ComparatorStock implementsComparator {

    publicint compare(Objecto1,Object o2){

       Stockstock1 = (Stock)o1;

       Stockstock2 = (Stock)o2;

       returnstock1.getTradingDate().compareTo(stock2.getTradingDate());

    }

}

 

6、使用Collections.sort完成List排序

    ComparatorStockcs = newComparatorStock();

    Collections.sort(list,cs);

 

7创建数据集,并根据排序后List内容设置数据集数据

// 构造一个数据集ds1,设置列名

       DataSetds1 = newDataSet("ds1");

       Stockcol = colList.get(0);

       ds1.addCol(col.getCode());

       ds1.addCol(col.getTradingDate());

       ds1.addCol(col.getPrice());

 

       // 设置数据集中的数据

       for (int i = 1; i <list.size(); i++) {

           Stockstock = list.get(i);

           com.runqian.report4.dataset.Rowrr = ds1.addRow();

           rr.setData(1,stock.getCode());

           rr.setData(2,stock.getTradingDate());

           rr.setData(3,stock.getPrice());

       }

 

       return ds1;

报表调用

    报表使用自定义数据集类型:

wKioL1P-yN6CjSuhAADWxZnm20g063.jpg

    设置报表模板及表达式:

wKioL1P-yN_g4lqlAABe0UQCzjw350.jpg

通过自定义数据集后在报表中调用可以完成本例的报表需求,润乾报表支持用户自定义数据集处理数据,体现了极大的灵活性。但过于依赖Java编程无疑增加了报表开发的难度,本例只是简单的完成了文件读取和排序,当包含分组、连接等计算时程序的复杂度会陡然上升,对报表开发人员来说都是不小的挑战。当然也可以用报表工具来完成排序等运算,但这样会导致占用较大的内存(特别是有过滤动作时,需要把数据都取到报表端再执行过滤),性能也会受到较大影响,有许多复杂的文件操作也会超出报表计算能力范围。

这种情况下,采用润乾报表基础上的计算强化版集算报表将是个不错的选择。集算报表内置的集算器对Excel文件读取的先天支持可以帮助集算报表快速完成Excel数据源报表。本例在集算报表中可以这样完成。

集算报表实现

    首先使用集算完成文件读入和排序,集算脚本如下:

wKiom1P-x8nTTnMcAACcHn6dpkg599.jpg

    A1:根据起始结束月份参数列出中间包含的月份

    A2:根据A1的计算结果,逐个读入月份文件,并将结果合并

    A3:为报表返回按照交易日期排序后结果

报表调用

数据集设置

    集算报表中使用集算器数据集类型,选择上面编辑好的集算器脚本文件(fromExcel.dfx):

wKioL1P-yOPgHvlBAADzf-z5R6U438.jpg

    其中be为报表参数,beginend为集算脚本参数.

 

报表模板及表达式

wKiom1P-x86whAZpAABQmpaJQkQ241.jpg

    通过上面的步骤可以看到,使用集算报表完成Excel数据源报表非常简单,既不需要大量的Java编程,也不必为到底选用何种方式(poijxljcom)操作Excel而费心。集算器脚本只需要3行即可完成计算过程,而同样的计算任务Java程序则需要几百行。集算报表让文件拥有了计算能力,使得开发文件数据源报表更为简单方便。

 

此外,如果集算脚本比较简单,可以直接使用集算报表内置的脚本数据集(一种数据集类型),将脚本直接嵌入到报表模板中,而不必在单独在集算器中完成。实现方式如下:

   1、在数据集设置窗口中点击“增加”按钮,弹出数据集类型对话框,选择脚本数据集”:

wKiom1P-x8_DdYjNAACFAqPGpK8357.jpg

    2、在弹出的脚本数据集编辑窗口中编写集算脚本:

wKioL1P-yOnR0kt8AAEcMaZPxV0617.jpg

    脚本数据集中可以直接使用报表定义的参数,如上述脚本中的beginend即为报表参数。 

    3、报表调用,与其他数据集使用方式一致,不再赘述。