帆软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 }  

这个示例我个人觉得有二个地方不太方便:
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 }

改进的地方:
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 }

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>

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

时间: 2024-10-05 02:51:00

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

报表开发工具对比系列之smartbi电子表格 vs 帆软finereport

报表是所有软件项目中的基础功能,市场上有不少报表工具,例如国外产品水晶报表.JasperReport.BIRT.jFreeReport等.不过由于国内市场的特殊性,报表格式通常比较复杂,老外的产品不太适应,现在开发者已逐渐转向国产的报表工具,例如Smartbi电子表格.润乾报表.帆软Finereport.久其报表等. 在国产的报表软件中,FineReport是老牌子,Smartbi电子表格是广州思迈特公司推出的新一代报表软件,这个“新一代”新在何处?有什么新的特色? Demo初体验两个产品都提供

帆软报表FineReport中数据连接之Websphere配置JNDI连接

以oracle9i数据源制作的模板jndi.cpt为例来说明如何在FineReport中的Websphere配置JNDI连接.由于常用服务器的JNDI驱动过大,帆软报表FineReport中没有自带,所以可以采取下面的方法来使用服务器中的JNDI连接.原理:先用JDBC连接到数据库,建立数据库连接,然后用SQL或者其他方法创建数据集,使用数据集制作报表,然后把建立的数据库连接从JDBC连接改成JNDI连接,然后配置好服务器中的JNDI连接,然后按照我们下面章节所说的部署好服务器,这样就可以通过W

帆软报表FineReport中数据连接之Tomcat配置JNDI连接

1. 问题描述 在帆软报表FineReport中,通过JNDI方式定义数据连接,首先在Tomcat服务器配置好JNDI,然后在设计器中直接调用JNDI的名字,即可成功使用JNDI连接,连接步骤如下: 2. 实现步骤 · 使用版本及环境 下面以Windows XP系统,tomcat 5.5,jdk 1.6,连接SQLserver2000数据库进行JNDI连接说明,其他版本数据库步骤基本相同. 2.1 拷贝驱动 将连接数据库的JDBC驱动拷贝到Tomcat安装目录下的%Tomcat_HOME%\co

帆软报表(finereport)入门-2 单元格中各颜色标识的含义

帆软报表(finereport)单元格中,可根据单元格角标的颜色判断单元格进行的操作 过滤:单元格左下角黄色三角形 条件属性:单元格左上角红色三角形.  控件:单元格右侧中间的各种矩形.  左父格:单元格左侧蓝色向下的箭头.  上父格:单元格上册蓝色向右的箭头.  原文地址:https://www.cnblogs.com/Williamls/p/10523949.html

帆软报表学习之数据连接

帆软报表FineReport中数据连接的JDBC连接池属性问题 连接池原理 在帆软报表FineReport中,连接池主要由三部分组成:连接池的建立.连接池中连接使用的治理.连接池的关闭.下面就着重讨论这三部分及连接池的配置问题. 1. 连接池原理 连接池技术的核心思想,是连接复用,通过建立一个数据库连接池以及一套连接使用.分配.治理策略,使得该连接池中的连接可以得到高效.安全的复用,避免了数据库连接频繁建立.关闭的开销. 另外,由于对JDBC中的原始连接进行了封装,从而方便了数据库应用对于连接的

帆软报表FineReport2016年1月份产品更新一览

1.条件属性可使用页码参数插件 由于报表计算逻辑关系,条件属性中取不到页码公式.但是有些场景下又是需要在条件属性中取到页码的,比如标题只要偶数页显示,比如奇数页标题标红等等. 插件安装完成后,条件属性里$$page_number$$totalPage_number也可以参与计算了,目前条件属性里可以设置边框.字体.新值.背景.颜色等样式. 注:目前只有样式可用,新值无法自动调整行高列宽 具体可查看文档:http://www.finereporthelp.com/help/15/2/22.html

帆软发布大数据直连引擎FineDirect,对焦大数据BI

摘要:近日,帆软官方正式发布大数据直连引擎FineDirect模块.通过该模块,企业在应用FineBI原有功能的基础上,可直接对接现有数据源,无论是传统的关系型数据库,还是Hadoop生态圈.Mpp构架,都可以直接自助取数分析. 当前,企业对数据的应用,一方面数据仓库和BI结合的方式仍占主导,另一方面越来越多的企业已逐渐引入大数据计算平台.个性化的方案.日益增长的数据,对BI工具的要求越来越高. Gartner也在2017年的BI报告中指出:未来5年,基于Hadoop/Spark,基于搜索和可视

地产cio揭秘:帆软大商业智能解决方案如何助力地产行业信息化

一.      地产行业信息化现状 房地产企业核心竞争能力的提升,需要强壮的企业运营管理能力,需要及时.准确.全面的业务数据分析作为参考与支撑.然而很多房地产企业缺乏能够集中体现企业运营活动状况的.全局的.直观的.可视化的房地产BI系统. 二.      帆软地产BI解决方案 1.         解决的问题 1)          系统繁多,数据收集效率低.业务数据分散在各个应用系统中,很难得到准确.统一.完整.直观,并能从各个业务主题与维度展现运营活动的管理数据 2)          各级

报表引擎API开发入门—简单程序数据集

小编最近接的项目是有关报表开发的,很想把这部分知识分享出来.希望大家能够支持我!不多说,马上进入我们今天的话题. API基本知识 小编最近项目所做的是关于一个报表软件-FineReport报表开发的一些事,也许有人接触过,知识相通,我今天就把我做的分享出来.FineReport设计器与服务器不可能满足所有的需求,某些个性化需求可能无法实现.我们可以深入的开发与控制API接口.想要了解我们内核结构及api的使用,首先我们必须掌握一些基本的概念,下图就是我们内核的最基本图示.图是网上找的,有点不清楚