Oracle存储过程和自定义函数
概述
Oracle-procedure解讀
Oracle存儲過程和自定義函數
PL/SQL中的過程和函數(通常稱為子程序)是PL/SQL塊的一種特殊的類型,這種類型的子程序可以以編譯的形式存放在數據庫中,并為后續的程序塊調用。
相同點: 完成特定功能的程序
不同點:是否用return語句返回值。
舉個例子:
create or replace procedure PrintStudents(p_staffName in xgj_test.username%type) ascursor c_testData isselect t.sal, t.comm from xgj_test t where t.username = p_staffName;beginfor v_info in c_testData loopDBMS_OUTPUT.PUT_LINE(v_info.sal || ' ' || v_info.comm);end loop;end PrintStudents;一旦創建了改程序并將其存儲在數據庫中,就可以使用如下的方式調用該過程
beginPrintStudents('Computer Science');PrintStudents('Match'); end; /或者
exec PrintStudents('Computer Science'); exec PrintStudents('Match');在命令窗口中:
在pl/sql工具的sql窗口中:
存儲過程的創建和調用
基本語法
create [ or replace] procedure procedure_name [( argument [ {IN | OUT | IN OUT }] type, ...... argument [ {IN | OUT | IN OUT }] type ) ] { IS | AS} procedure_body無參的存儲過程
/**無參數的存過打印hello world調用存儲過程:1. exec sayhelloworld();2 begin sayhelloworld();end;/*/ create or replace procedure sayhelloworld as --說明部分 begindbms_output.put_line('hello world'); end sayhelloworld;調用過程:
SQL> set serveroutput on ; SQL> exec sayhelloworld();hello worldPL/SQL procedure successfully completedSQL> begin2 sayhelloworld();3 sayhelloworld();4 end;5 /hello world hello worldPL/SQL procedure successfully completed帶參數的存儲過程
/** 創建一個帶參數的存儲過程給指定的員工增加工資,并打印增長前后的工資*/ create or replace procedure addSalary(staffName in xgj_test.username%type ) as --定義一個變量保存調整之前的薪水 oldSalary xgj_test.sal%type;begin--查詢員工漲之前的薪水select t.sal into oldSalary from xgj_test t where t.username=staffName; --調整薪水update xgj_test t set t.sal = sal+1000 where t.username=staffName ;--輸出dbms_output.put_line('調整之前的薪水:'|| oldSalary || ' ,調整之后的薪水:' || (oldSalary + 1000));end addSalary;可以看到,update語句之后并沒有commit的操作。
一般來講為了保證事務的一致性,由調用者來提交比較合適,當然了是需要區分具體的業務需求的~
存儲函數
基本語法
create [ or replace] function function_name [( argument [ {IN | OUT | IN OUT }] type, ...... argument [ {IN | OUT | IN OUT }] type ) ] RETURN { IS | AS} function_body其中 return子句是必須存在的,一個函數如果沒有執行return就結束將發生錯誤,這一點和存過有說不同。
存儲函數
準備的數據如下:
存在一個問題,當獎金為空的時候,算出來的年收入竟然是空的。
因為 如果一個表達式中有空值,那么這個表達式的結果即為空值。
所以我們需要對空值進行處理, 使用nvl函數即可。
最后修改后的function為
create or replace function querySalaryInCome(staffName in varchar2)return number as--定義變量保存員工的工資和獎金pSalary xgj_test.sal%type;pComm xgj_test.comm%type;begin--查詢員工的工資和獎金select t.sal, t.comminto pSalary, pCommfrom xgj_test twhere t.username = staffName;--直接返回年薪return pSalary * 12 + nvl(pComm,0); end querySalaryInCome;out參數
out參數
一般來講,存儲過程和存儲函數的區別在于存儲函數可以有一個返回值,而存儲過程沒有返回值。
- 存儲過程和存儲函數都可以有out參數
- 存儲過程和存儲函數都可以有多個out參數
- 存儲過程可以通過out參數實現返回值
那我們如何選擇存儲過程和存儲函數呢?
原則:
如果只有一個返回值,用存儲函數,否則(即沒有返回值或者有多個返回值)使用存儲過程。
/** 根據員工姓名,查詢員工的全部信息 */ create or replace procedure QueryStaffInfo(staffName in xgj_test.username%type,pSal out number,pComm out xgj_test.comm%type,pJob out xgj_test.job%type) isbegin--查詢該員工的薪資,獎金和職位select t.sal,t.comm,t.job into pSal,pComm,pJob from xgj_test t where t.username=staffName; end QueryStaffInfo;先拋出兩個思考問題:
- 查詢員工的所有信息–> out參數太多怎么辦?
- 查詢某個部門中所有員工的信息–> out中返回集合?
后面會講到如何解決? 總不能一個個的寫out吧~
在應用中訪問存儲過程和存儲函數
概述
我們使用JAVA程序連接ORACLE數據庫。
使用jar: ojdbc14.jar
關于oracle官方提供的幾個jar的區別
classes12.jar (1,600,090 bytes) - for use with JDK 1.2 and JDK 1.3
classes12_g.jar (2,044,594 bytes) - same as classes12.jar, except that classes were compiled with “javac -g” and contain some tracing information.
classes12dms.jar (1,607,745 bytes) - same as classes12.jar, except that it contains additional code`to support Oracle Dynamic Monitoring Service.
classes12dms_g.jar (2,052,968 bytes) - same as classes12dms.jar except that classes were compiled with “javac -g” and contain some tracing information.
ojdbc14.jar (1,545,954 bytes) - classes for use with JDK 1.4 and 1.5
ojdbc14_g.jar (1,938,906 bytes) - same as ojdbc14.jar, except that classes were compiled with “javac -g” and contain some tracing information.
ojdbc14dms.jar (1,553,561 bytes) - same as ojdbc14.jar, except that it contains additional code`to support Oracle Dynamic Monitoring Service.
ojdbc14dms_g.jar (1,947,136 bytes) - same as ojdbc14dms.jar, except that classes were compiled with “javac -g” and contain some tracing information.
工程目錄如下:
簡單的寫下獲取數據庫連接的工具類
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;public class DBUtils {// 設定數據庫驅動,數據庫連接地址端口名稱,用戶名,密碼private static final String driver = "oracle.jdbc.driver.OracleDriver";private static final String url = "jdbc:oracle:thin:@ip:xxxx";private static final String username = "xxxx";private static final String password = "xxxx";/*** 注冊數據庫驅動*/static {try {Class.forName(driver);} catch (ClassNotFoundException e) {throw new ExceptionInInitializerError(e.getMessage());}}/*** 獲取數據庫連接*/public static Connection getConnection() {try {Connection connection = DriverManager.getConnection(url, username, password);// 成功,返回connectionreturn connection;} catch (SQLException e) {e.printStackTrace();}// 獲取失敗,返回nullreturn null;}/*** 釋放連接*/public static void cleanup(Connection conn, Statement st, ResultSet rs) {if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();} finally {rs = null;}}if (st != null) {try {st.close();} catch (SQLException e) {e.printStackTrace();} finally {st = null;}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();} finally {conn = null;}}} }在應用程序中訪問存儲過程
根據官方提供的API,我們可以看到:
在應用程序中訪問存儲函數
import java.sql.CallableStatement; import java.sql.Connection;import org.junit.Test;import com.turing.oracle.dbutil.DBUtils;import oracle.jdbc.OracleTypes;public class TestFuction {@Testpublic void callFuction(){//{?= call <procedure-name>[(<arg1>,<arg2>, ...)]}Connection conn = null;CallableStatement call = null;/*** create or replace function querySalaryInCome(staffName in varchar2)return number as--定義變量保存員工的工資和獎金pSalary xgj_test.sal%type;pComm xgj_test.comm%type;begin--查詢員工的工資和獎金select t.sal, t.comminto pSalary, pCommfrom xgj_test twhere t.username = staffName;--直接返回年薪return pSalary * 12 + nvl(pComm,0);end querySalaryInCome;*/String sql = "{?=call querySalaryInCome(?)}";try {// 獲取連接conn = DBUtils.getConnection();// 通過conn獲取CallableStatementcall = conn.prepareCall(sql);// out 參數,需要聲明call.registerOutParameter(1, OracleTypes.NUMBER);// in 參數,需要賦值call.setString(2, "gong");// 執行call.execute();// 取出返回值 第一個?的值double income = call.getDouble(1);System.out.println("該員工的年收入:" + income);} catch (Exception e) {e.printStackTrace();}finally {DBUtils.cleanup(conn, call, null);}}}在out參數中訪問光標
在out參數中使用光標
我們之前拋出的兩個思考問題:
- 查詢員工的所有信息–> out參數太多怎么辦?
- 查詢某個部門中所有員工的信息–> out中返回集合?
我們可以通過返回Cursor的方式來實現。
在out參數中使用光標 的步驟:
- 申明包結構
- 包頭
- 包體
包頭:
create or replace package MyPackage is-- Author : ADMINISTRATOR-- Created : 2016-6-4 18:10:42-- Purpose : -- 使用type關鍵字 is ref cursor說明是cursor類型type staffCursor is ref cursor;procedure queryStaffJob(pJob in xgj_test.job%type,jobStaffList out staffCursor);end MyPackage;創建完包頭之后,創建包體,包體需要實現包頭中聲明的所有方法。
包體
create or replace package body MyPackage isprocedure queryStaffJob(pJob in xgj_test.job%type,jobStaffList out staffCursor)asbeginopen jobStaffList for select * from xgj_test t where t.job=pJob;end queryStaffJob;end MyPackage;事實上,通過plsql工具創建包頭,編譯后,包體的框架就會自動的生成了。
在應用程序中訪問包下的存儲過程
在應用程序中訪問包下的存儲過程
在應用程序中訪問包下的存儲過程 ,需要帶包名
import java.sql.CallableStatement; import java.sql.Connection; import java.sql.ResultSet;import org.junit.Test;import com.turing.oracle.dbutil.DBUtils;import oracle.jdbc.OracleTypes; import oracle.jdbc.driver.OracleCallableStatement;public class TestCursor {@Testpublic void testCursor(){/*** * create or replace package MyPackage istype staffCursor is ref cursor;procedure queryStaffJob(pJob in xgj_test.job%type,jobStaffList out staffCursor);end MyPackage;*/String sql = "{call MyPackage.queryStaffJob(?,?)}" ;Connection conn = null;CallableStatement call = null ;ResultSet rs = null;try {// 獲取數據庫連接conn = DBUtils.getConnection();// 通過conn創建CallableStatemetcall = conn.prepareCall(sql);// in 參數 需要賦值call.setString(1, "Staff");// out 參數需要聲明call.registerOutParameter(2, OracleTypes.CURSOR);// 執行調用call.execute();// 獲取返回值rs = ((OracleCallableStatement)call).getCursor(2);while(rs.next()){// 取出值String username = rs.getString("username");double sal = rs.getDouble("sal");double comm = rs.getDouble("comm");System.out.println("username:" + username + "\t sal:" + sal + "\t comm:" + comm);}} catch (Exception e) {e.printStackTrace();}finally {DBUtils.cleanup(conn, call, rs);}}}總結
以上是生活随笔為你收集整理的Oracle存储过程和自定义函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Gson-更新中
- 下一篇: Oracle-trigger触发器解读