首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 調用 Oracle 分頁存儲過程完整實戰:PageInfo 封裝與 Hibernate 整合

     編輯:Java 相關知識

前言

海量數據查詢場景下,分頁是必須解決的問題。本文展示 Java 調用 Oracle 存儲過程實現高效分頁的完整方案,包含分頁參數封裝類 PageInfo、Hibernate 獲取原生連線調用 CallableStatement、結果集映射為 List 等核心環節。

分頁參數封裝類:PageInfo

標準化分頁參數傳遞,包含表名、查詢條件、排序字段、排序方式、當前頁、頁大小、總記錄數、總頁數等完整屬性,提供全參構造器與無參構造器,符合 JavaBean 規範。

public class PageInfo {
    private String p_tableName;      // 表名
    private String p_strWhere;       // 查詢條件
    private String p_orderColumn;    // 排序列
    private String p_orderStyle;     // 排序方式 (ASC/DESC)
    private int p_curPage;           // 當前頁
    private int p_pageSize;          // 頁大小
    private int p_totalRecords;      // 總記錄數 (輸出參數)
    private int p_totalPages;        // 總頁數 (輸出參數)

    // 無參構造器
    public PageInfo() {}

    // 全參構造器
    public PageInfo(String p_tableName, String p_strWhere,
                    String p_orderColumn, String p_orderStyle, int p_curPage,
                    int p_pageSize, int p_totalRecords, int p_totalPages) {
        this.p_tableName = p_tableName;
        this.p_strWhere = p_strWhere;
        this.p_orderColumn = p_orderColumn;
        this.p_orderStyle = p_orderStyle;
        this.p_curPage = p_curPage;
        this.p_pageSize = p_pageSize;
        this.p_totalRecords = p_totalRecords;
        this.p_totalPages = p_totalPages;
    }

    // Getter/Setter 省略...
}

存儲過程調用核心類:GeneratePage

使用 Hibernate Session 獲取底層 JDBC Connection,準備 CallableStatement 調用 Oracle 存儲過程 pages.prc_page。關鍵點:註冊輸出參數類型 (OracleTypes.NUMBER, OracleTypes.CURSOR)、獲取 REF CURSOR 並轉換為 List 返回。

public class GeneratePage {
    public static ArrayList Prc_Page(PageInfo page) {
        ArrayList list = new ArrayList();
        Map mp;
        Session s = null;
        Connection conn = null;
        ResultSet rs = null;
        CallableStatement proc = null;
        
        try {
            s = HibernateSessionFactory.getSession();
            conn = s.connection();
            
            // 調用存儲過程:9個參數 (7輸入 + 2輸出 + 1游標輸出)
            proc = conn.prepareCall("{call pages.prc_page(?,?,?,?,?,?,?,?,?)}");
            proc.setString(1, page.getP_tableName());
            proc.setString(2, page.getP_strWhere());
            proc.setString(3, page.getP_orderColumn());
            proc.setString(4, page.getP_orderStyle());
            proc.setInt(5, page.getP_curPage());
            proc.setInt(6, page.getP_pageSize());
            
            // 註冊輸出參數
            proc.registerOutParameter(7, OracleTypes.NUMBER);   // 總記錄數
            proc.registerOutParameter(8, OracleTypes.NUMBER);   // 總頁數
            proc.registerOutParameter(9, OracleTypes.CURSOR);   // 結果游標
            
            proc.execute();
            
            // 獲取輸出參數回填 PageInfo
            page.setP_totalRecords(proc.getInt(7));
            page.setP_totalPages(proc.getInt(8));
            
            // 獲取 REF CURSOR 並轉換
            rs = ((OracleCallableStatement) proc).getCursor(9);
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            
            while (rs.next()) {
                mp = new HashMap(columnCount);
                for (int r = 1; r <= columnCount; r++) {
                    mp.put(rsmd.getColumnName(r), rs.getObject(r));
                }
                list.add(mp);
            }
            return list;
            
        } catch (SQLException ex) {
            ex.printStackTrace();
            return list;
        } catch (Exception ex2) {
            ex2.printStackTrace();
            return list;
        } finally {
            // 資源釋放順序:Statement -> ResultSet -> Connection
            try {
                if (proc != null) proc.close();
                if (rs != null) rs.close();
                if (conn != null) conn.close();
            } catch (SQLException ex1) {
                ex1.printStackTrace();
            }
        }
    }
}

Oracle 存儲過程參數規範 (pages.prc_page)

參數位置參數名類型模式說明
1p_tableNameVARCHAR2IN查詢表名
2p_strWhereVARCHAR2INWHERE 條件子句 (不含 WHERE 關鍵字)
3p_orderColumnVARCHAR2IN排序字段名
4p_orderStyleVARCHAR2IN排序方向 ASC/DESC
5p_curPageNUMBERIN當前頁碼 (從 1 開始)
6p_pageSizeNUMBERIN每頁記錄數
7p_totalRecordsNUMBEROUT返回總記錄數
8p_totalPagesNUMBEROUT返回總頁數
9v_curREF CURSOROUT返回分頁結果集游標

關鍵技術點解析

  • Hibernate 獲取原生連線:session.connection() (Hibernate 3) 或 session.doReturningWork() (Hibernate 5+)
  • OracleTypes 常量:需引入 oracle.jdbc.driver.OracleTypes 與 oracle.jdbc.OracleCallableStatement
  • REF CURSOR 處理:強轉為 OracleCallableStatement 調用 getCursor(int) 獲取 ResultSet
  • 結果集元數據:使用 ResultSetMetaData 動態獲取列名與列數,實現通用 Map 映射
  • 資源釋放順序:嚴格遵循 先開後關 原則,finally 塊確保異常時也能釋放

現代化改造建議

  1. Spring Data JPA 整合:使用 @Procedure 調用存儲過程,自動映射輸出參數
  2. 連線池優化:配置 HikariCP 替換 Hibernate 內建連線池
  3. 泛型強化:PageInfo 引入泛型 <T>,返回 List<T> 而非原始 List
  4. 分頁對象標準化:採用 Spring Data Page<T> 或 PageImpl<T> 替代自定義 PageInfo
  5. SQL 注入防護:p_strWhere 參數若拼接用戶輸入,必須嚴格驗證或使用參數化查詢
  6. 異步非阻塞:引入 CompletableFuture 或 Reactor Mono 實現非阻塞調用

常見問題排查

異常現象可能原因解決方案
Invalid column typeregisterOutParameter 類型不匹配確認 OracleTypes 常量與存儲過程定義一致
Closed Connection連線池回收導致連線失效檢查連線池驗證查詢、idleTimeout 配置
Cursor already closedResultSet 使用前連線已關閉確保在 finally 關閉連線前處理完游標數據
ORA-06550存儲過程不存在/參數錯誤驗證存儲過程名稱、參數個數、類型、順序
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved