Oracle存储过程和自定义函数
概述
Oracle-procedure解讀
Oracle存儲過程和自定義函數(shù)
PL/SQL中的過程和函數(shù)(通常稱為子程序)是PL/SQL塊的一種特殊的類型,這種類型的子程序可以以編譯的形式存放在數(shù)據(jù)庫中,并為后續(xù)的程序塊調(diào)用。
相同點(diǎn): 完成特定功能的程序
不同點(diǎn):是否用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;一旦創(chuàng)建了改程序并將其存儲在數(shù)據(jù)庫中,就可以使用如下的方式調(diào)用該過程
beginPrintStudents('Computer Science');PrintStudents('Match'); end; /或者
exec PrintStudents('Computer Science'); exec PrintStudents('Match');在命令窗口中:
在pl/sql工具的sql窗口中:
存儲過程的創(chuàng)建和調(diào)用
基本語法
create [ or replace] procedure procedure_name [( argument [ {IN | OUT | IN OUT }] type, ...... argument [ {IN | OUT | IN OUT }] type ) ] { IS | AS} procedure_body無參的存儲過程
/**無參數(shù)的存過打印hello world調(diào)用存儲過程:1. exec sayhelloworld();2 begin sayhelloworld();end;/*/ create or replace procedure sayhelloworld as --說明部分 begindbms_output.put_line('hello world'); end sayhelloworld;調(diào)用過程:
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帶參數(shù)的存儲過程
/** 創(chuàng)建一個帶參數(shù)的存儲過程給指定的員工增加工資,并打印增長前后的工資*/ create or replace procedure addSalary(staffName in xgj_test.username%type ) as --定義一個變量保存調(diào)整之前的薪水 oldSalary xgj_test.sal%type;begin--查詢員工漲之前的薪水select t.sal into oldSalary from xgj_test t where t.username=staffName; --調(diào)整薪水update xgj_test t set t.sal = sal+1000 where t.username=staffName ;--輸出dbms_output.put_line('調(diào)整之前的薪水:'|| oldSalary || ' ,調(diào)整之后的薪水:' || (oldSalary + 1000));end addSalary;可以看到,update語句之后并沒有commit的操作。
一般來講為了保證事務(wù)的一致性,由調(diào)用者來提交比較合適,當(dāng)然了是需要區(qū)分具體的業(yè)務(wù)需求的~
存儲函數(shù)
基本語法
create [ or replace] function function_name [( argument [ {IN | OUT | IN OUT }] type, ...... argument [ {IN | OUT | IN OUT }] type ) ] RETURN { IS | AS} function_body其中 return子句是必須存在的,一個函數(shù)如果沒有執(zhí)行return就結(jié)束將發(fā)生錯誤,這一點(diǎn)和存過有說不同。
存儲函數(shù)
準(zhǔn)備的數(shù)據(jù)如下:
存在一個問題,當(dāng)獎金為空的時候,算出來的年收入竟然是空的。
因?yàn)?如果一個表達(dá)式中有空值,那么這個表達(dá)式的結(jié)果即為空值。
所以我們需要對空值進(jìn)行處理, 使用nvl函數(shù)即可。
最后修改后的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參數(shù)
out參數(shù)
一般來講,存儲過程和存儲函數(shù)的區(qū)別在于存儲函數(shù)可以有一個返回值,而存儲過程沒有返回值。
- 存儲過程和存儲函數(shù)都可以有out參數(shù)
- 存儲過程和存儲函數(shù)都可以有多個out參數(shù)
- 存儲過程可以通過out參數(shù)實(shí)現(xiàn)返回值
那我們?nèi)绾芜x擇存儲過程和存儲函數(shù)呢?
原則:
如果只有一個返回值,用存儲函數(shù),否則(即沒有返回值或者有多個返回值)使用存儲過程。
/** 根據(jù)員工姓名,查詢員工的全部信息 */ 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參數(shù)太多怎么辦?
- 查詢某個部門中所有員工的信息–> out中返回集合?
后面會講到如何解決? 總不能一個個的寫out吧~
在應(yīng)用中訪問存儲過程和存儲函數(shù)
概述
我們使用JAVA程序連接ORACLE數(shù)據(jù)庫。
使用jar: ojdbc14.jar
關(guān)于oracle官方提供的幾個jar的區(qū)別
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.
工程目錄如下:
簡單的寫下獲取數(shù)據(jù)庫連接的工具類
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;public class DBUtils {// 設(shè)定數(shù)據(jù)庫驅(qū)動,數(shù)據(jù)庫連接地址端口名稱,用戶名,密碼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";/*** 注冊數(shù)據(jù)庫驅(qū)動*/static {try {Class.forName(driver);} catch (ClassNotFoundException e) {throw new ExceptionInInitializerError(e.getMessage());}}/*** 獲取數(shù)據(jù)庫連接*/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;}}} }在應(yīng)用程序中訪問存儲過程
根據(jù)官方提供的API,我們可以看到:
在應(yīng)用程序中訪問存儲函數(shù)
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 參數(shù),需要聲明call.registerOutParameter(1, OracleTypes.NUMBER);// in 參數(shù),需要賦值call.setString(2, "gong");// 執(zhí)行call.execute();// 取出返回值 第一個?的值double income = call.getDouble(1);System.out.println("該員工的年收入:" + income);} catch (Exception e) {e.printStackTrace();}finally {DBUtils.cleanup(conn, call, null);}}}在out參數(shù)中訪問光標(biāo)
在out參數(shù)中使用光標(biāo)
我們之前拋出的兩個思考問題:
- 查詢員工的所有信息–> out參數(shù)太多怎么辦?
- 查詢某個部門中所有員工的信息–> out中返回集合?
我們可以通過返回Cursor的方式來實(shí)現(xiàn)。
在out參數(shù)中使用光標(biāo) 的步驟:
- 申明包結(jié)構(gòu)
- 包頭
- 包體
包頭:
create or replace package MyPackage is-- Author : ADMINISTRATOR-- Created : 2016-6-4 18:10:42-- Purpose : -- 使用type關(guān)鍵字 is ref cursor說明是cursor類型type staffCursor is ref cursor;procedure queryStaffJob(pJob in xgj_test.job%type,jobStaffList out staffCursor);end MyPackage;創(chuàng)建完包頭之后,創(chuàng)建包體,包體需要實(shí)現(xiàn)包頭中聲明的所有方法。
包體
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;事實(shí)上,通過plsql工具創(chuàng)建包頭,編譯后,包體的框架就會自動的生成了。
在應(yīng)用程序中訪問包下的存儲過程
在應(yīng)用程序中訪問包下的存儲過程
在應(yīng)用程序中訪問包下的存儲過程 ,需要帶包名
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 {// 獲取數(shù)據(jù)庫連接conn = DBUtils.getConnection();// 通過conn創(chuàng)建CallableStatemetcall = conn.prepareCall(sql);// in 參數(shù) 需要賦值call.setString(1, "Staff");// out 參數(shù)需要聲明call.registerOutParameter(2, OracleTypes.CURSOR);// 執(zhí)行調(diào)用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);}}}總結(jié)
以上是生活随笔為你收集整理的Oracle存储过程和自定义函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Gson-更新中
- 下一篇: Oracle-trigger触发器解读