帆软FineReport如何使用程序数据集

大多数情况下,FineReport直接在设计器里使用“数据集查询”,直接写SQL就能满足报表要求,但对于一些复杂的报表,有时候SQL处理并不方便,这时可以把查询结果在应用层做一些预处理后,再传递给报表,即所谓的“程序数据集”,FineReport的帮助文档上给了一个示例:

  1 package com.fr.data;
  2
  3 import java.sql.Connection;
  4 import java.sql.DriverManager;
  5 import java.sql.ResultSet;
  6 import java.sql.ResultSetMetaData;
  7 import java.sql.Statement;
  8 import java.util.ArrayList;
  9 import com.fr.base.FRContext;
 10 import com.fr.data.AbstractTableData;
 11 import com.fr.base.Parameter;
 12
 13 public class ParamTableDataDemo extends AbstractTableData {
 14     // 列名数组,保存程序数据集所有列名
 15     private String[] columnNames = null;
 16     // 定义程序数据集的列数量
 17     private int columnNum = 10;
 18     // 保存查询表的实际列数量
 19     private int colNum = 0;
 20     // 保存查询得到列值
 21     private ArrayList valueList = null;
 22
 23     // 构造函数,定义表结构,该表有10个数据列,列名为column#0,column#1,。。。。。。column#9
 24     public ParamTableDataDemo() {
 25         // 定义tableName参数
 26         this.parameters = new Parameter[] { new Parameter("tableName") };
 27         // 定义程序数据集列名
 28         columnNames = new String[columnNum];
 29         for (int i = 0; i < columnNum; i++) {
 30             columnNames[i] = "column#" + String.valueOf(i);
 31         }
 32     }
 33
 34     // 实现其他四个方法
 35     public int getColumnCount() {
 36         return columnNum;
 37     }
 38
 39     public String getColumnName(int columnIndex) {
 40         return columnNames[columnIndex];
 41     }
 42
 43     public int getRowCount() {
 44         init();
 45         return valueList.size();
 46     }
 47
 48     public Object getValueAt(int rowIndex, int columnIndex) {
 49         init();
 50         if (columnIndex >= colNum) {
 51             return null;
 52         }
 53         return ((Object[]) valueList.get(rowIndex))[columnIndex];
 54     }
 55
 56     // 准备数据
 57     public void init() {
 58         // 确保只被执行一次
 59         if (valueList != null) {
 60             return;
 61         }
 62         // 保存得到的数据库表名
 63         String tableName = parameters[0].getValue().toString();
 64         // 构造SQL语句,并打印出来
 65         String sql = "select * from " + tableName + ";";
 66         FRContext.getLogger().info("Query SQL of ParamTableDataDemo: \n" + sql);
 67         // 保存得到的结果集
 68         valueList = new ArrayList();
 69         // 下面开始建立数据库连接,按照刚才的SQL语句进行查询
 70         Connection conn = this.getConnection();
 71         try {
 72             Statement stmt = conn.createStatement();
 73             ResultSet rs = stmt.executeQuery(sql);
 74             // 获得记录的详细信息,然后获得总列数
 75             ResultSetMetaData rsmd = rs.getMetaData();
 76             colNum = rsmd.getColumnCount();
 77             // 用对象保存数据
 78             Object[] objArray = null;
 79             while (rs.next()) {
 80                 objArray = new Object[colNum];
 81                 for (int i = 0; i < colNum; i++) {
 82                     objArray[i] = rs.getObject(i + 1);
 83                 }
 84                 // 在valueList中加入这一行数据
 85                 valueList.add(objArray);
 86             }
 87             // 释放数据库资源
 88             rs.close();
 89             stmt.close();
 90             conn.close();
 91             // 打印一共取到的数据行数量
 92             FRContext.getLogger().info(
 93                     "Query SQL of ParamTableDataDemo: \n" + valueList.size()
 94                             + " rows selected");
 95         } catch (Exception e) {
 96             e.printStackTrace();
 97         }
 98     }
 99
100     // 获取数据库连接 driverName和 url 可以换成您需要的
101     public Connection getConnection() {
102         String driverName = "sun.jdbc.odbc.JdbcOdbcDriver";
103         String url = "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=D:\\FineReport_7.0\\WebReport\\FRDemo.mdb";
104         String username = "";
105         String password = "";
106         Connection con = null;
107         try {
108             Class.forName(driverName);
109             con = DriverManager.getConnection(url, username, password);
110         } catch (Exception e) {
111             e.printStackTrace();
112             return null;
113         }
114         return con;
115     }
116
117     // 释放一些资源,因为可能会有重复调用,所以需释放valueList,将上次查询的结果释放掉
118     public void release() throws Exception {
119         super.release();
120         this.valueList = null;
121     }
122 }  

View Code

这个示例我个人觉得有二个地方不太方便:
1、db连接串硬编码写死在代码里,维护起来不太方便,目前大多数b/s应用,对于数据库连接,通常是利用spring在xml里配置datasource bean,运行时动态注入

2、将查询出的结果,填充到数据集时,采用的是数字索引(见82行),代码虽然简洁,但是可读性比较差

折腾一番后,于是便有了下面的改进版本:

  1 package infosky.ckg.fr.data;
  2
  3 import infosky.ckg.utils.AppContext;
  4 import java.sql.Connection;
  5 import java.sql.ResultSet;
  6 import java.sql.Statement;
  7 import java.util.LinkedHashMap;
  8 import java.util.LinkedHashSet;
  9 import javax.sql.DataSource;
 10 import com.fr.base.Parameter;
 11 import com.fr.data.AbstractTableData;
 12 import com.fr.general.data.TableDataException;
 13
 14 public class ParameterLinkedHashSetDataDemo extends AbstractTableData {
 15
 16     private static final long serialVersionUID = 8818000311745955539L;
 17
 18     // 字段名枚举
 19     enum FIELD_NAME {
 20         EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY
 21     }
 22
 23     private String[] columNames;
 24
 25     private LinkedHashSet<LinkedHashMap<String, Object>> rowData;
 26
 27     public ParameterLinkedHashSetDataDemo() {
 28         this.parameters = new Parameter[] { new Parameter("jobId"),
 29                 new Parameter("minSalary"), new Parameter("maxSalary") };
 30
 31         // 填充字段名
 32         columNames = new String[FIELD_NAME.values().length];
 33         int i = 0;
 34         for (FIELD_NAME fieldName : FIELD_NAME.values()) {
 35             columNames[i] = fieldName.toString();
 36             i++;
 37         }
 38
 39     }
 40
 41     @Override
 42     public int getColumnCount() throws TableDataException {
 43         return columNames.length;
 44     }
 45
 46     @Override
 47     public String getColumnName(int columnIndex) throws TableDataException {
 48         return columNames[columnIndex];
 49     }
 50
 51     @Override
 52     public int getRowCount() throws TableDataException {
 53         queryData();
 54         return rowData.size();
 55     }
 56
 57     @Override
 58     public Object getValueAt(int rowIndex, int columnIndex) {
 59         queryData();
 60         int tempRowIndex = 0;
 61         for (LinkedHashMap<String, Object> row : rowData) {
 62             if (tempRowIndex == rowIndex) {
 63                 return row.get(columNames[columnIndex]);
 64             }
 65             tempRowIndex += 1;
 66         }
 67         return null;
 68     }
 69
 70     // 查询数据
 71     private void queryData() {
 72         // 确保只被执行一次
 73         if (rowData != null) {
 74             return;
 75         }
 76
 77         // 传入的参数
 78         String jobId = parameters[0].getValue().toString();
 79         float minSalary = Float.parseFloat(parameters[1].getValue().toString());
 80         float maxSalary = Float.parseFloat(parameters[2].getValue().toString());
 81
 82         // 拼装SQL
 83         String sql = "select * from EMPLOYEES where JOB_ID='" + jobId
 84                 + "' and SALARY between " + minSalary + " and " + maxSalary;
 85
 86         rowData = new LinkedHashSet<LinkedHashMap<String, Object>>();
 87
 88         Connection conn = this.getConnection();
 89         try {
 90             Statement stmt = conn.createStatement();
 91             // 执行查询
 92             ResultSet rs = stmt.executeQuery(sql);
 93             while (rs.next()) {
 94                 // 填充行数据
 95                 // 注:字段赋值的顺序,要跟枚举里的顺序一样
 96                 LinkedHashMap<String, Object> row = new LinkedHashMap<String, Object>();
 97                 row.put(FIELD_NAME.EMPLOYEE_ID.toString(),
 98                         rs.getInt(FIELD_NAME.EMPLOYEE_ID.toString()));
 99                 row.put(FIELD_NAME.FIRST_NAME.toString(),
100                         rs.getString(FIELD_NAME.FIRST_NAME.toString()));
101                 row.put(FIELD_NAME.LAST_NAME.toString(),
102                         rs.getString(FIELD_NAME.LAST_NAME.toString()));
103                 row.put(FIELD_NAME.EMAIL.toString(),
104                         rs.getString(FIELD_NAME.EMAIL.toString()));
105                 row.put(FIELD_NAME.PHONE_NUMBER.toString(),
106                         rs.getString("PHONE_NUMBER"));
107                 row.put(FIELD_NAME.HIRE_DATE.toString(),
108                         rs.getDate(FIELD_NAME.HIRE_DATE.toString()));
109                 row.put(FIELD_NAME.JOB_ID.toString(),
110                         rs.getString(FIELD_NAME.JOB_ID.toString()));
111                 row.put(FIELD_NAME.SALARY.toString(),
112                         rs.getFloat(FIELD_NAME.SALARY.toString()));
113                 rowData.add(row);
114             }
115             rs.close();
116             stmt.close();
117             conn.close();
118         } catch (Exception e) {
119             e.printStackTrace();
120         }
121
122     }
123
124     // 获取数据库连接
125     private Connection getConnection() {
126         Connection con = null;
127         try {
128             DataSource dataSource = AppContext.getInstance().getAppContext()
129                     .getBean("dataSource", DataSource.class);
130             con = dataSource.getConnection();
131         } catch (Exception e) {
132             e.printStackTrace();
133             return null;
134         }
135         return con;
136     }
137
138     // 释放资源
139     public void release() throws Exception {
140         super.release();
141         this.rowData = null;
142     }
143
144 }

View Code

改进的地方:
1、getConnection方法,利用Spring注入datasource,当然为了注入方便,还需要一个辅助类AppContext

 1 package infosky.ckg.utils;
 2
 3 import org.springframework.context.support.AbstractApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5
 6 public class AppContext {
 7     private static AppContext instance;
 8
 9     private AbstractApplicationContext appContext;
10
11     public synchronized static AppContext getInstance() {
12         if (instance == null) {
13             instance = new AppContext();
14         }
15         return instance;
16     }
17
18     private AppContext() {
19         this.appContext = new ClassPathXmlApplicationContext(
20                 "spring/root-context.xml");
21     }
22
23     public AbstractApplicationContext getAppContext() {
24         return appContext;
25     }
26
27 }

View Code

classes/spring/root-context.xml 里配置db连接

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xsi:schemaLocation="http://www.springframework.org/schema/beans
 5     http://www.springframework.org/schema/beans/spring-beans.xsd">
 6
 7     <bean id="dataSource"
 8         class="org.springframework.jdbc.datasource.DriverManagerDataSource">
 9         <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
10
11         <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE" />
12         <property name="username" value="hr" />
13         <property name="password" value="hr" />
14     </bean>
15 </beans>

View Code

2、将原来的数组,换成了LinkedHashSet<LinkedHashMap<String, Object>>,这样db查询结果填充到"数据集"时,处理代码的可读性就多好了(见queryData方法),但也要注意到LinkedHashSet/LinkedHashMap的性能较Array而言,有所下降,正所谓:有所得必有得失。但对于复杂的汇总统计报表,展示的数据通常不会太多,所以这个问题我个人看来并不严重。

 

时间: 2024-11-03 17:42:10

帆软FineReport如何使用程序数据集的相关文章

帆软(FineReport)报表工具试用评测报告

报表工具FineReport(帆软),该产品在ITLE监控产品remedy中被使用.针对渠道部报表和客服部报表的应用场景,特抽取几个有代表性的点进行尝试.几点心得,和大家分享一下(为今后统一报表平台做铺垫). 1.&http://www.aliyun.com/zixun/aggregation/37954.html">nbsp; 安装下载试用方法a)下载地址:http://www.finereport.com/cn/trial.html b)免费激活码:AKMO-A 88A 20-

百家银行CIO聚焦“转型突围&quot; 帆软引领银行价值峰会“迷茫”中寻出路

4月7-9日,由帆软软件有限公司主办的"第二届亚太银行价值峰会"在江苏天目湖畔的涵田度假村酒店召开.红塔商业银行.灌云农商行.南通农商行.法国贸易银行上海分行.美国银行.平安银行总行科技部(上海).中信银行山西省分行.邳州农商行.长江商业银行.中信银行苏州分行河北银行.威海市商业银行.浙江民泰银行.天津渤海银行.浦发银行上海支行.浦发硅谷银行有限公司.九江银行.江阴农商行.淮海农商行.中国人民银行征信中心.绍兴嵊州农商行.温州龙湾农商行.大连银行.江苏银行.常熟农商银行等全国117家银

帆软CEO陈炎:为什么大量的数据分析项目会失败?

今年9月份Gartner发布的<传统企业报表平台市场指南>里,有唯一一家中国公司入选了由它推荐的供应商列表名单--帆软旗下报表类产品Finereport,和微软.SAP.Oracle等国际巨头并列,由此引起了早餐君的注意. 简要回顾下,2006年,还在南京大学的陈炎和另外两位志同道合的同龄人成立了帆软.最初以报表工具起家,后拓展至商业智能平台.目前提供的产品服务主要是以私有云部署的Finereport和FineBI,以及公有云部署的简道云.经过多年奋斗,帆软已经成为专注于商用报表系统和自助大数

帆软大数据巡展嘉兴站现场干货精选

"让企业数据成为生产力,帆软不仅要做而且要做得最好".3月17日,在浙江嘉兴召开的帆软百城巡展嘉兴站上,区域经理李虹达这样讲到,"帆软一直在数据领域精耕细作,从报表方案.大数据BI方案一直到数据应用构建方案.目前也正逢市场的风口,我们有责任也有担当去引领行业的发展. 众所周知,移动互联与工业4.0催生了大数据时代的发展,也是未来趋势.很多企业在治理自身IT的同时也在不断摸索,如何利用大数据,制定有效的数据管理方案,提升管理效率,降低运营成本. 企业构建大数据"又痛又

帆软荣获2016”IT印象”最具影响力商业智能品牌

 2017年初,中国企业"IT印象·技术成就梦想"年终评选榜单出炉,作为国内领先的商业智能和数据分析平台提供商,帆软(www.fanruan.com)凭借过去一年在商业智能领域的杰出贡献,荣获2016年最具影响力的商业智能品牌奖. 据悉,该活动由工业和信息化部中国电子信息产业发展研究院指导,51CTO主办,旨在评选出最具广泛应用和优秀IT解决方案的品牌.围绕行业影响力.品牌知名度.产品创新力.方案落地性和技术服务能力,帆软获得各位专家评审及行业媒体的认可,在评选中脱颖而出. 帆软成立至

帆软6 2-在帆软6.2里,定义数据库连接的时候,连接不上特定模式

问题描述 在帆软6.2里,定义数据库连接的时候,连接不上特定模式 数据库连接成功后,默认显示dbo下面的Tables,我想显示另一个模式pts下面的表,如何显示 解决方案 ?你可以联系一下客服问一下,我一直用finereport报表工具,感觉它的功能还是蛮强大的. 解决方案二: 你可以联系一下客服问一下,我一直用finereport报表工具,感觉它的功能还是蛮强大的.

为新零售赋能!帆软携百位零售CIO探讨数据价值

随着"新零售"的概念业内快速兴起,无论是马云提出的线上线下和物流的结合,还是京东苏宁大力开设实体店.成本上升.人口红利消失.电商渗透率饱和都在倒逼零售的整体升级.而大数据的收集与运用是新零售的根基,消费变革的起点一定在离消费者最近的地方,其中最关键的一环,就是要提升自身的数据能力,真正实现以用户体验为中心的经营模式. 5月17-19日,由帆软举办的"新零售.新资源.新挑战"零售IT大会在南京召开,会议邀请了万达.银泰.华联.大商等近百家零售电商百货企业.作为一场围绕

帆软2017百城巡展启动在即 掀数据化管理之风

如今,大数据应用在不断摸索和发展,企业数据化管理的方式由提出到教化也在不断实践.面对前有互联网电商对大数据的高度应用,后有企业管理落后竞争力下降的忧患夹击,企业对于数据利用的"热趋势"是取还是舍,同样的模式如何应用到自身情况来达到"数据变现"? 近日,由帆软发起和主办的"让数据成为生产力"2017百城巡展活动即将拉开序幕,聚焦企业的数据化管理.据悉,此次巡展活动将走过北京.上海.广州.深圳.杭州.南京.武汉.重庆.厦门.合肥.石家庄在内的30多个

ie 8-ie8 下帆软页面空白 打印功能也是空白

问题描述 ie8 下帆软页面空白 打印功能也是空白 用ie8 进入帆软页面空白 但是点击一下 其他页面在回来 就可以看到 其他浏览器直接就可以. 打印的时候也是空白,点击其他页面在回来 打印页面出来了 但是都页面是灰的 什么都看到不了 但是用其他浏览器 就不会有问题了