文章出處
View Code
View Code
View Code
View Code
文章列表
大多數情況下,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而言,有所下降,正所謂:有所得必有得失。但對于復雜的匯總統計報表,展示的數據通常不會太多,所以這個問題我個人看來并不嚴重。
文章列表
全站熱搜