title: JSP頁面查詢顯示常用模式 author: evan email: evan_zhao@hotmail.com 背景: 1. 需要將資料庫查詢結果在JSP中以列表方式顯示 2. 在一個良好的J2EE模式中資料庫查詢一般用DAO實現(Data Access Object), JSP僅用於顯示資料 問題: 通過JDBC ResultSet可擷取查詢結果(存在於資料庫緩衝區內),但在Statement、Connection關閉後ResultSet即不可用。因此需要一種方式取出所有查詢結果並傳遞至JSP頁面。 解決方案一: 使用Value Object。將每條記錄均封裝成JavaBean對象,把這些對象裝入Collection傳送給JSP顯示。這種方法的缺點是每一種查詢都需要定義一個java class,並且將記錄資料封裝成java對象時也需要很多額外的代碼。 範例程式碼: //查詢資料代碼 Connection conn = DBUtil.getConnection(); PreparedStatement pst = null; ResultSet rs = null; try{ String sql=“select emp_code, real_name from t_employee where organ_id=?”; pst = conn.preparedStatement(sql); pst.setString(1, “101”); ResultSet rs = pst.executeQuery(); List list = new ArrayList(); Employee emp; while (rs.next()){ emp = new Employee(); emp.setReakName(rs.getString(“real_name”)); emp.setEmpCode(rs.getString(“emp_code”)); … list.add(emp); } return list; }finally{ DBUtil.close(rs, pst ,conn); } //jsp顯示部分代碼 <% List empList = (List)request.getAttribute(“empList”); if (empList == null) empList = Collections.EMPTY_LIST; %> … <table cellspacing="0" width=”90%”> <tr> <td>代碼</td> <td>姓名</td> </tr> <% Employee emp; for (int i=0; i< empList.size(); i++){ emp = (Employee) empList.get(i); %> <tr> <td><%= emp.getEmpCode()%></td> <td><%= emp.getRealName()%></td> </tr> <% }// end for %> </table> 解決方案二: 遍曆ResultSet取出所有資料封裝進Collection。 具體做法: 1. 產生一個List對象(List list = new ArrayList() )。 2. 產生一個Map對象(Map map = new HashMap() )。使用Map封裝一行資料,key為各欄位名,value為對應的值。(map.put(“USER_NAME”), rs.getString(“USER_NAME”)) 3. 將第2 步產生的Map對象裝入第1步的list對象中(list.add(map) )。 4. 重複2、3步直到ResultSet遍曆完畢 在DBUtil. resultSetToList(ResultSet rs)方法中實現了上述過程(所有列名均使用大寫),可參考使用。 範例程式碼: //查詢資料部分代碼: … Connection conn = DBUtil.getConnection(); PreparedStatement pst = null; ResultSet rs = null; try{ String sql=“select emp_code, real_name from t_employee where organ_id=?”; pst = conn.preparedStatement(sql); pst.setString(1, “101”); rs = pst.executeQuery(); List list = DBUtil. resultSetToList(ResultSet rs); return list; }finally{ DBUtil.close(rs, pst ,conn); } //JSP顯示部分代碼 <% List empList = (List)request.getAttribute(“empList”); if (empList == null) empList = Collections.EMPTY_LIST; %> … <table cellspacing="0" width=”90%”> <tr> <td>代碼</td> <td>姓名</td> </tr> <% Map colMap; for (int i=0; i< empList.size(); i++){ colMap = (Map) empList.get(i); %> <tr> <td><%=colMap.get(“EMP_CODE”)%></td> <td><%=colMap.get(“REAL_NAME”)%></td> </tr> <% }// end for %> </table> 解決方案三: 使用RowSet。 RowSet是JDBC2.0中提供的介面,Oracle對該介面有相應實現,其中很有用的是oracle.jdbc.rowset.OracleCachedRowSet。 OracleCachedRowSet實現了ResultSet中的所有方法,但與ResultSet不同的是,OracleCachedRowSet中的資料在Connection關閉後仍然有效。 oracle的rowset實現在http://otn.oracle.com/software/content.html的jdbc下載裡有,名稱是ocrs12.zip 範例程式碼: //查詢資料部分代碼: import javax.sql.RowSet; import oracle.jdbc.rowset.OracleCachedRowSet; … Connection conn = DBUtil.getConnection(); PreparedStatement pst = null; ResultSet rs = null; try{…… String sql=“select emp_code, real_name from t_employee where organ_id=?”; pst = conn.preparedStatement(sql); pst.setString(1, “101”); rs = pst.executeQuery(); OracleCachedRowSet ors = newOracleCachedRowSet(); //將ResultSet中的資料封裝到RowSet中 ors.populate(rs); return ors; }finally{ DBUtil.close(rs, pst, conn); } //JSP顯示部分代碼 <% javax.sql.RowSet empRS = (javax.sql.RowSet) request.getAttribute(“empRS”); %> … <table cellspacing="0" width=”90%”> <tr> <td>代碼</td> <td>姓名</td> </tr> <% if (empRS != null) while (empRS.next() ) { %> <tr> <td><%= empRS.get(“EMP_CODE”)%></td> <td><%= empRS.get(“REAL_NAME”)%></td> </tr> <% }// end while %> </table> 適用場合: 方法一使用於定製的查詢操作 方法二適用於多條查詢語句或需要對查詢結果進行處理的情況。 方法三適合於單條查詢語句,適用於快速開發。 相關連結: 如果需要分頁顯示請參考:JSP分頁技術實現 如果查詢結果需要產生WORD或者EXCEL,請參考:使用jsp實現word、excel格式報表列印 附:DBUtil代碼: import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.Properties; import java.util.Collections; import java.sql.Connection; import java.sql.SQLException; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.Statement; import java.sql.PreparedStatement; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; public class DBUtil{ private static final String JDBC_DATA_SOURCE = "java:comp/env/jdbc/DataSource"; /** enableLocalDebug: 是否在本地調試。<br> 值為true時如果尋找資料來源失敗則使用DriverManager與資料庫建立串連; 如果為false則只尋找資料來源建立資料庫連接。 預設為false。<br> 可通過系統屬性jdbc.enable_local_debug=true設定enableLocalDebug為true,啟用本地調試:<br> 增加JVM parameter: -Djdbc.enable_local_debug=true */ private static boolean enableLocalDebug = false; static{ enableLocalDebug = Boolean.getBoolean ("jdbc.enable_local_debug"); } private static Context ctx = null; private static javax.sql.DataSource ds = null; private static void initDataSource() throws Exception{ // Put connection properties in to a hashtable. if (ctx == null) { ctx = new InitialContext(); } if (ds == null) { ds = (javax.sql.DataSource) ctx.lookup(JDBC_DATA_SOURCE); } } /** * 尋找應用伺服器資料來源,從資料來源中獲得資料庫連接。<br><br> * 在本地調試時如果尋找資料來源失敗並且enableLocalDebug==true * 則根據系統屬性使用java.sql.DriverManager建立串連。<br> * 本地調試時可配置的系統屬性如下:<br> * <p> * #jdbc驅動程式名 <br> * jdbc.driver=<i>oracle.jdbc.driver.OracleDriver</i> <br> <br> * #資料庫連接串<br> * jdbc.url=<i>jdbc:oracle:thin:@10.1.1.1:1521:ocrl</i> <br> <br> * #資料庫使用者名稱<br> * jdbc.username=<i>scott</i> <br> <br> * #資料庫使用者密碼<br> * jdbc.password=<i>tiger</i> <br> * </p> * 可通過JVM參數設定上述系統屬性:<br> * -Djdbc.driver=oracle.jdbc.driver.OracleDriver * -Djdbc.url=jdbc:oracle:thin:@10.1.1.1:1521:ocrl * -Djdbc.username=scott -Djdbc.password=tiger * @return Connection * @throws NamingException 如果資料來源尋找失敗 * @throws SQLException 如果建立資料庫連接失敗 */ public static Connection getConnection() throws SQLException{ try{ initDataSource(); return ds.getConnection(); }catch(SQLException sqle){ throw sqle; }catch(Exception ne){ if (enableLocalDebug){ return getTestConn(); }else{ throw new RuntimeException(ne.toString()); } } } //通過DriverManager建立本地測試連接 private static Connection getTestConn(){ try { String driver = System.getProperty("jdbc.driver"); System.out.println("jdbc.driver="+driver); String url = System.getProperty("jdbc.url"); System.out.println("jdbc.url="+url); String userName = System.getProperty("jdbc.username"); System.out.println("jdbc.username="+userName); String password = System.getProperty("jdbc.password"); System.out.println("jdbc.password="+password); Class.forName(driver).newInstance(); return java.sql.DriverManager.getConnection(url, userName, password); } catch (Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex.getMessage()); } } /** * 將查詢結果封裝成List。<br> * List中元素類型為封裝一行資料的Map,Map key為欄位名(大寫),value為相應欄位值 * @param rs ResultSet * @return List * @throws java.sql.SQLException */ public static List resultSetToList(ResultSet rs) throws java.sql.SQLException{ if (rs==null) return Collections.EMPTY_LIST; ResultSetMetaData md = rs.getMetaData(); int columnCount = md.getColumnCount(); List list = new ArrayList(); Map rowData; while (rs.next()){ rowData = new HashMap(columnCount); for (int i=1; i<=columnCount; i++){ rowData.put(md.getColumnName(i),rs.getObject(i)); } list.add(rowData); } return list; } /** * 關閉ResultSet、Statement和Connection * @param rs ResultSet to be closed * @param stmt Statement or PreparedStatement to be closed * @param conn Connection to be closed */ public static void close(ResultSet rs, Statement stmt, Connection conn){ if (rs != null) try{ rs.close(); }catch(java.sql.SQLException ex){ ex.printStackTrace(); } if (stmt != null) try{ stmt.close(); }catch(java.sql.SQLException ex){ ex.printStackTrace(); } if (conn != null) try{ conn.close(); }catch(java.sql.SQLException ex){ ex.printStackTrace(); } } }// end of DBUtil |