存儲過程結果集jdbc
1. PHP調用存儲過程的結果集如何非同步展示在前端表格中
本文闡述了怎麼使用DBMS存儲過程。我闡述了使用存儲過程的基本的和高級特性,比如返回ResultSet。本文假設你對DBMS和JDBC已經非常熟悉,也假設你能夠毫無障礙地閱讀其它語言寫成的代碼(即不是java的語言),但是,並不要求你有任何存儲過程的編程經歷。
存儲過程是指保存在資料庫並在資料庫端執行的程序。你可以使用特殊的語法在Java類中調用存儲過程。在調用時,存儲過程的名稱及指定的參數通過JDBC連接發送給DBMS,執行存儲過程並通過連接(如果有)返回結果。
使用存儲過程擁有和使用基於EJB或CORBA這樣的應用伺服器一樣的好處。區別是存儲過程可以從很多流行的DBMS中免費使用,而應用伺服器大都非常昂貴。這並不只是許可證費用的問題。使用應用伺服器所需要花費的管理、編寫代碼的費用,以及客戶程序所增加的復雜性,都可以通過DBMS中的存儲過程所整個地替代。
你可以使用Java,Python,Perl或C編寫存儲過程,但是通常使用你的DBMS所指定的特定語言。Oracle使用PL/sql,PostgreSQL使用pl/pgsql,DB2使用Proceral SQL。這些語言都非常相似。在它們之間移植存儲過程並不比在Sun的EJB規范不同實現版本之間移植Session Bean困難。並且,存儲過程是為嵌入SQL所設計,這使得它們比Java或C等語言更加友好地方式表達資料庫的機制。
因為存儲過程運行在DBMS自身,這可以幫助減少應用程序中的等待時間。不是在Java代碼中執行4個或5個SQL語句,而只需要在伺服器端執行1個存儲過程。網路上的數據往返次數的減少可以戲劇性地優化性能。
使用存儲過程
簡單的老的JDBC通過CallableStatement類支持存儲過程的調用。該類實際上是PreparedStatement的一個子類。假設我們有一個poets資料庫。資料庫中有一個設置詩人逝世年齡的存儲過程。下面是對老酒鬼Dylan Thomas(old soak Dylan Thomas,不指定是否有關典故、文化,請批評指正。譯注)進行調用的詳細代碼:
try{
int age = 39;
String poetName = "dylan thomas";
CallableStatement proc = connection.prepareCall("{ call set_death_age(?, ?) }");
proc.setString(1, poetName);
proc.setInt(2, age);
cs.execute();
}catch (SQLException e){ // ....}
傳給prepareCall方法的字串是存儲過程調用的書寫規范。它指定了存儲過程的名稱,?代表了你需要指定的參數。
和JDBC集成是存儲過程的一個很大的便利:為了從應用中調用存儲過程,不需要存根(stub)類或者配置文件,除了你的DBMS的JDBC驅動程序外什麼也不需要。
當這段代碼執行時,資料庫的存儲過程就被調用。我們沒有去獲取結果,因為該存儲過程並不返回結果。執行成功或失敗將通過例外得知。失敗可能意味著調用存儲過程時的失敗(比如提供的一個參數的類型不正確),或者一個應用程序的失敗(比如拋出一個例外指示在poets資料庫中並不存在「Dylan Thomas」)
結合SQL操作與存儲過程
映射Java對象到SQL表中的行相當簡單,但是通常需要執行幾個SQL語句;可能是一個SELECT查找ID,然後一個INSERT插入指定ID的數據。在高度規格化(符合更高的範式,譯注)的資料庫模式中,可能需要多個表的更新,因此需要更多的語句。Java代碼會很快地膨脹,每一個語句的網路開銷也迅速增加。
將這些SQL語句轉移到一個存儲過程中將大大簡化代碼,僅涉及一次網路調用。所有關聯的SQL操作都可以在資料庫內部發生。並且,存儲過程語言,例如PL/SQL,允許使用SQL語法,這比Java代碼更加自然。下面是我們早期的存儲過程,使用Oracle的PL/SQL語言編寫:
create procere set_death_age(poet VARCHAR2, poet_age NUMBER)
poet_id NUMBER;
begin SELECT id INTO poet_id FROM poets WHERE name = poet;
INSERT INTO deaths (mort_id, age) VALUES (poet_id, poet_age);
end set_death_age;
很獨特?不。我打賭你一定期待看到一個poets表上的UPDATE。這也暗示了使用存儲過程實現是多麼容易的一件事情。set_death_age幾乎可以肯定是一個很爛的實現。我們應該在poets表中添加一列來存儲逝世年齡。Java代碼中並不關心資料庫模式是怎麼實現的,因為它僅調用存儲過程。我們以後可以改變資料庫模式以提高性能,但是我們不必修改我們代碼。
下面是調用上面存儲過程的Java代碼:
public static void setDeathAge(Poet dyingBard, int age) throws SQLException{
Connection con = null;
CallableStatement proc = null;
try {
con = connectionPool.getConnection();
proc = con.prepareCall("{ call set_death_age(?, ?) }");
proc.setString(1, dyingBard.getName());
proc.setInt(2, age);
proc.execute();
}
finally {
try { proc.close(); }
catch (SQLException e) {}
con.close();
}
2. JDBC調用Sybase存儲過程,結果集總是無法返回,該怎麼處理
JDBC調用Sybase存儲過程,結果集總是無法返回!
java代碼部分如下:
String sproc = "{ call zhouxiaobotest2(?,?,?,?)} ";
CommonDAO = null;
try {
= new CommonDAO(jndi);
Connection connect = .getConn();
//得到總數
rowNum = PageDiv.getCount(countSql.toString(), );
//設置分頁信息
pageBean.setPageInfo(rowNum, pageSize);
// 獲取CallableStatement語句:
CallableStatement mStatement = connect.prepareCall(sproc,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
// 傳入輸入參數和注冊輸出參數
mStatement.setString(1,sql.toString());
mStatement.setInt(2, 10);
mStatement.setInt(3, 10);
mStatement.setInt(4, 5000);
// 執行存儲過程
ResultSet rs = null;
mStatement.execute();
rs = mStatement.getResultSet();
catch (Exception ex) {
ex.printStackTrace();
logger.warn(sql.toString());
} finally {
.destroy();
= null;
sproc = null;
}
存儲過程如下:
create procere zhouxiaobotest @qry varchar(16384),@ipage int, @num int,@maxpages int
as
/*@qry SQL語句, @ipage 頁數, @num 每頁記錄條數, @maxpages 最大查詢頁數 */
begin
declare @execsql varchar(16384)
set @execsql = " select USER_NAME from TBL_USER "
execute (@execsql)
end
create procere zhouxiaobotest2 @qry varchar(16384),@ipage int, @num int,@maxpages int
as
/*@qry SQL語句, @ipage 頁數, @num 每頁記錄條數, @maxpages 最大查詢頁數 */
begin
select USER_NAME from TBL_USER
end
現在我的問題是調用存儲過程zhouxiaobotest,mStatement.execute()始終是false;而調用zhouxiaobotest2 mStatement.execute()就是true,能得到結果集。我想知道如何修改程序使我能夠調用zhouxiaobotest取得結果集(之前調試程序運行沒有拋出任何異常,而且控制台運行存儲過程也都正確)
3. java調用存儲過程返回遊標的結果集無法關閉游標的問題
在過程里判斷很簡單呀
CREATE OR REPLACE PROCEDURE TESTPROC(CUR OUT SYS_REFCURSOR)
AS
default_c SYS_REFCURSOR;
BEGIN
OPEN CUR FOR SELECT * FROM AA;
if CUR%rowcount = 0 then
CUR:=default_c;
end if;
END;
定義一個默認空游標,沒數據就返回它唄
4. 徹底搞懂JDBC的運行過程
前幾天筆者發布了博客,手寫mybatis徹底搞懂框架原理。為了幫助初學者更好理解mybatis框架,這次講解一下Java的JDBC的運行過程。
JDBC的作用
JDBC的全稱是Java DataBase Connection,也就是Java資料庫連接,我們可以用它來操作關系型資料庫。JDBC介面及相關類在java.sql包和javax.sql包里。我們可以用它來連接資料庫,執行SQL查詢,存儲過程,並處理返回的結果。
JDBC介面讓Java程序和JDBC驅動實現了松耦合,使得切換不同的資料庫變得更加簡單。
JDBC的連接步驟
執行一次JDBC連接,分六個步驟進行:
1. 導入包
在程序中包含資料庫編程所需的JDBC類。大多數情況下,使用 import java.sql.* 就足夠了
2. 注冊JDBC驅動程序
需要初始化驅動程序,這樣就可以打開與資料庫的通信。
3. 打開一個連接
使用DriverManager.getConnection()方法來創建一個Connection對象,它代表一個資料庫的物理連接。
4. 執行一個查詢
需要使用一個類型為Statement或PreparedStatement的對象(兩者區別看後文),並提交一個SQL語句到資料庫執行查詢。
5. 從結果集中提取數據
這一步中演示如何從資料庫中獲取查詢結果的數據。使用ResultSet.getXXX()方法來檢索的數據結果
6. 清理環境資源
在使用JDBC與數據交互操作資料庫中的數據後,應該明確地關閉所有的資料庫資源以減少資源的浪費。本文使用了try with resources方式關閉資源,這是JDK7的語法糖,讀者可自行搜索。
完整代碼如下。
JDBC的最佳實踐
JDBC是如何實現Java程序和JDBC驅動的松耦合?
JDBC API使用Java的反射機制來實現Java程序和JDBC驅動的松耦合。看一下上文的JDBC示例,你會發現所有操作都是通過JDBC介面完成的,而驅動只有在通過Class.forName反射機制來載入的時候才會出現。
這是Java核心庫里反射機制的最佳實踐之一,它使得應用程序和驅動程序之間進行了隔離,讓遷移資料庫的工作變得更簡單。
Statement和PreparedStatement區別
預編譯
創建時的區別:
執行時的區別:
由上可以看出,PreparedStatement有預編譯的過程,已經綁定sql,之後無論執行多少次,都不會再去進行編譯,而Statement 不同,如果執行多次,則相應的就要編譯多少次sql,所以從這點看,PreparedStatement的效率會比Statement要高一些。PreparedStatement是預編譯的,所以可以有效的防止SQL注入等問題
佔位符
PrepareStatement可以替換變數在SQL語句中可以包含?,可以用?替換成變數。
而Statement只能用字元串拼接。
JDBC的ResultSet
在查詢資料庫後會返回一個ResultSet,它就像是查詢結果集的一張數據表。
ResultSet對象維護了一個游標,指向當前的數據行。開始的時候這個游標指向的是第一行。如果調用了ResultSet的next()方法游標會下移一行,如果沒有更多的數據了,next()方法會返回false。可以在for循環中用它來遍歷數據集。
默認的ResultSet是不能更新的,游標也只能往下移。也就是說你只能從第一行到最後一行遍歷一遍。不過也可以創建可以回滾或者可更新的ResultSet,像下面這樣。
當生成ResultSet的Statement對象要關閉或者重新執行或是獲取下一個ResultSet的時候,ResultSet對象也會自動關閉。
可以通過ResultSet的getter方法,傳入列名或者從1開始的序號來獲取列數據。
ResultSet的不同類型
根據創建Statement時輸入參數的不同,會對應不同類型的ResultSet。如果你看下Connection的方法,你會發現createStatement和prepareStatement方法重載了,以支持不同的ResultSet和並發類型。
ResultSet對象有三種類型。
ResultSet有兩種並發類型。
5. java jdbc 如何從存儲過程的結果集中獲取列名
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM TABLE2");
ResultSetMetaData rsmd = rs.getMetaData();
//獲取第一列的列名
String name = rsmd.getCatalogName(1);
6. Java JDBC里如何取得Oracle存儲過程返回的動態結果集
創建存儲過程
createorreplaceprocereproc3(stidinstudent.stuid%type,stnameoutstudent.sname%type,stphoneoutstudent.phonenumber%type,stuaddoutstudent.saddress%type)
ascountnumbernumber;
begin
selectcount(*)=stid;
ifcountnumber=1then
=stid;
=stid;
=stid;
else
dbms_output.put_line('返回值過多');
endif;
end;調用存儲過程時,要用CallabelStatement的prepareCall 方法。結構:{call 存儲過程名(?,?,...)}
在設置參數的時候,輸入參數用set,輸出參數要registerOutParameter。取出輸出參數的值可以直接用CallabelStatement的get方法
importjava.sql.CallableStatement;
importjava.sql.Connection;
importjava.sql.DriverManager;
importjava.sql.ResultSet;
importjava.sql.SQLException;
importjava.sql.Types;
publicclassDao{
Stringdriver="oracle.jdbc.driver.OracleDriver";
Stringurl="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
Connectionconn=null;
CallableStatementcs=null;//PreparedStatement,Statement
ResultSetrs;
publicvoidgetConn(){
try{
Class.forName(driver);
conn=DriverManager.getConnection(url,"scott","tiger");
}catch(ClassNotFoundExceptione){
e.printStackTrace();
}catch(SQLExceptione){
e.printStackTrace();
}
}
publicvoidcallProc(){
try{
cs=conn.prepareCall("{callproc3(?,?,?,?)}");
cs.setInt(1,1);
cs.registerOutParameter(2,Types.VARCHAR);
cs.registerOutParameter(3,Types.VARCHAR);
cs.registerOutParameter(4,Types.VARCHAR);
cs.execute();
Stringname=cs.getString(2);
Stringphone=cs.getString(3);
Stringaddress=cs.getString(4);
System.out.println("Name:"+name+" Phone:"+phone+" Address:"+address);
}catch(SQLExceptione){
e.printStackTrace();
}finally{
try{
if(cs!=null)cs.close();
if(conn!=null)conn.close();
}catch(SQLExceptione){
e.printStackTrace();
}
}
}
publicstaticvoidmain(String[]args){
Dao=newDao();
.getConn();
.callProc();
}
}