Oracle教程第五篇
阿新 • • 發佈:2019-01-12
一、遊標
1.簡介
oracle 讀取資料的工具,類似java集合。
2.示例
DECLART -- 獲取班級集合 CURSOR EMPLIST IS SELECT * FROM EMP ; -- 定義emp 表的物件 進行解析 V_EMPOBJ EMP%ROWTYPE; BEGIN -- 解析遊標(集合) -- 1. 開啟遊標。 OPEN EMPLIST; LOOP EXIT WHEN EMPLIST%NOTFOUND FETCH EMPLIST INTO V_EMPOBJ; dbms_output.put_line(v_empobj.empno||v_empobj.ename); END LOOP; -- 2. 關閉遊標 CLOSE EMPLIST; END
3.簡化版
DECLARE
CURSOR GLIST IS SELECT * FROM GRADE;
BEGIN
FOR g IN GLIST LOOP
dbms_output.put_line(g.gid);
END LOOP;
END
二、事務
1.一個程式是否健壯,主要看異常處理機制
2.異常
- oracle 一共定義有2萬個錯誤。
- 預定義錯誤 彈窗 終止程式執行。
- 處理異常。
EXCEPTION WHEN TOO_MANY_ROWS THEN dbms_output.put_line('返回條數過多,不能賦值'); WHEN NO_DATA_FOUND THEN dbms_output.put_line('要查詢的資料不存在');
- 自定義異常。
-- 如果有員工工資小於1000 丟擲異常提示。 DECLARE CURSOR emplist IS SELECT * FROM EMP ; -- 1. 定義異常 SAL_TOO_LOW EXCEPTION; BEGIN FOR e IN emplist LOOP IF e.sal < 1000 THEN -- 2. 引發異常 RAISE SAL_TOO_LOW; END IF; END LOOP; -- 3. 接收並處理異常 EXCEPTION WHEN SAL_TOO_LOW THEN dbms_output.put_line('有員工工資太低'); raise_application_error(-20001,'工資過低,請及時漲工資'); //錯誤資訊標號 必須小於 -20000 END
- 其他異常
所有未攔截的異常都應該被others接收
WHEN OTHERS THEN
dbms_output.put_line('所有未攔截的異常');
空白R攔截順序
先是預定義 再是自定義 最後是others
三、儲存過程
儲存過程是一個有名字的 plsql 塊
1. 示例
//-->根據員工編號給指定員工漲工資,並列印相關資訊。
create or replace procedure updateempbyempno(
eno emp.empno%type //沒有;
) as
--宣告變數的地方
a_sal emp.sal%type ; --漲工資之前
b_sal emp.sal%type;--漲工資之後
begin
select sal into a_sal from emp where empno = eno;
dbms_output.put_line('漲工資之前為'||a_sal);
--漲工資
update emp set sal=sal+100 where empno = eno;
commit;
--查詢漲了之後的工資
select sal into b_sal from emp where empno = eno;
dbms_output.put_line('漲工資之後為' || b_sal);
end;
-- 呼叫儲存過程
begin
updateempbyempno(7521);
end;
四、儲存函式
1.示例
根據員工編號查詢員工的年薪。
create or replace function FINDSALBYEMPNO(eno EMP.SAL%TYPE)
RETURN NUMBER // eno不能和欄位名一致
AS
v_nianxin number(11,2);
BEGIN
-- 查詢年薪
select sal*12+nvl(comm,0) into v_nianxin from emp where empno=eno;
return v_nianxin;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('沒有找到資料');
WHEN TOO_MANY_ROWS THEN
dbms_output.put_line('輸出資料過多');
END;
-- 呼叫儲存函式
DECLARE
--定義方法返回值的接受變數
v_sal number(11,2);
BEGIN
v_sal := FINDSALBYEMPNO(7521);
dbms_output.put_line('年薪為'|| v_sal);
END
五、多返回值的儲存過程
1.示例1
根據編號返回員工名稱和年薪
create or replace procedure findempinfobyempno(
eno in emp.empno%type, [in 輸入模式]
empname out emp.ename%type,[out 輸出模式]
nianxin out number [不是表中的型別,不需要帶長度]
)
as
begin
select ename,sal*12+nvl(comm,0) into empname,nianxin
from emp where empno = eno;
end;
--呼叫帶out的儲存過程
DECLARE
ename emp.ename%type;
nianxin number(11,2)
BEGIN
findempinfobyempno(7521,ename,nianxin);
dbms_output.put_line(ename||'的年薪是:'||nianxin);
END;
2.示例2
獲取所有人的年薪。//引數為遊標型別
create or replace procedure
findALlSal(allsal out sys_refcursor)as
begin
open allsal for select sal*12+nvl(comm.0) from emp;
end;
-- 呼叫儲存過程
DECLARE
--儲存遊標型別所有的年薪
ALLSAL SYS_REFCURSOR;
SAL EMP.SAL%TYPE;
BEGIN
--呼叫
FINDALLSAL(ALLSAL);
--迴圈
LOOP
--出口
EXIT WHEN ALLSAL%NOTFOUND;
--列印
DBMS_OUTPUT.PUT_LINE(SAL);
--注入
FETCH ALLSAL INTO SAL;
END LOOP;
--結束迴圈
CLOSE ALLSAL;
--關閉遊標
END;
3.總結
當儲存過程的引數型別是遊標型別時,開啟遊標時設定查詢結果進去。
因為在定義儲存過程時將遊標打開了,所以呼叫儲存過程時不能用for迴圈
而且記得關閉遊標。
六、java呼叫儲存過程
1.驅動包
2.工具類
- 四個屬性
driver:oracle.jdbc.OracleDriver
url:jdbc:oracle:thin:@192.168.56.130:1521:orcl
user:test_302
password:ORCL - 呼叫儲存過程
//需求:根據某個人的id 獲取他的年薪
@Test
public void testGetSomeOneYearNew() throws Exception{
//獲取連結
Connection conn = JdbcUtil.getConn();
//sql語句 呼叫儲存過程 用{call 儲存過程名} ?佔位符的索引從1開始
String sql = "{call FINDALLINFOBYNO(?,?,?)}";
//獲取欲執行語句
CallableStatement statement = conn.prepareCall(sql);
statement.setInt(1, 7521);
//傳出模式 引數得註冊
statement.registerOutParameter(2,OracleTypes.VARCHAR);
statement.registerOutParameter(3,OracleTypes.NUMBER);
//執行
statement.execute();
//處理結果集
//獲取佔位符的值
System.out.println(statement.getString(2)+"的年薪為:"+statement.getDouble(3));
//釋放連結
JdbcUtil.closeResource(conn, statement, null);
}
七、綜合示例
1.分頁獲取學生資料,包含學生以及班級資訊
- 儲存過程
CREATE OR REPLACE PROCEDURE FINDSTUDENTSBYPAGE(
PI IN NUMBER,PS IN NUMBER,STUDENTLIST OUT SYS_REFCURSOR)
AS
BEGIN
OPEN STUDENTLIST FOR
SELECT * FROM (
SELECT A.*,ROWNUM RN FROM
(SELECT S.*,G.G_NAME,G.G_DESC FROM STUDENT S INNER JOIN GRADE G ON S.G_ID = G.G_ID ORDER BY S.G_ID ASC,S.S_ID ASC) A) B WHERE B.RN BETWEEN (PI-1)*PS+1 AND PI*PS;
END;
- 客戶端來呼叫儲存過程
DECLARE
studentlist SYS_REFCURSOR;
sname student.s_name%TYPE;
gname grade.g_name%TYPE;
BEGIN
FINDSTUDENTSBYPAGE(2,10,studentlist);
-- 解析遊標
LOOP
EXIT WHEN studentlist%NOTFOUND;
dbms_output.put_line('學生姓名:'||sname||'所屬班級'||gname);
FETCH studentlist INTO sname,gname;
END LOOP;
CLOSE studentlist;
END;
- java程式碼來呼叫儲存過程
@Test
public void testGetStudentByPage() throws Exception{
//獲取連結
Connection conn = JdbcUtil.getConn();
String sql="{call FINDSTUDENTBYPAGE(?,?,?)}";
//獲取欲執行語句
CallableStatement st = conn.prepareCall(sql);
//--給引數賦值
st.setInt(1, 2);
st.setInt(2, 10);
st.registerOutParameter(3, OracleTypes.CURSOR);
//執行
st.execute();
//處理結果集
ResultSet rs = ((OracleCallableStatement)st).getCursor(3);
//--遍歷取值
while(rs.next()){
System.out.println(
rs.getObject(1)+"\t"+
rs.getObject(2)+"\t"+
rs.getObject(4));
System.out.println();
}
//釋放資源
JdbcUtil.closeResource(conn, st, rs);
}
八、觸發器
1.觸發器的型別
- 語句級觸發器。
語句每執行一次就會執行一次。 - 行級的觸發器。
2.語句級的觸發器。
- 示例1
// 插入班級資訊以後,列印新增成功。
create or replace trigger gradetg1
after insert on grade
declare
begin
dbms_output.put_line("新增成功");
end;
// 自己插入一條資訊測試。
- 示例2
不能再星期四 新增班級資訊。
create or replace tirgger gradetg2
before insert on grade
declare`在這裡插入程式碼片`
v_week varchar(32);
BEGIN
SELECT TO_CHAR(SYSDATE,'DAY') INTO V_WEEK FROM DUAL;
IF V_WEEK = '星期四' THEN
Raise_application_error(-20001,'星期四不能新增班級!');
END IF;
END;
3.行級的觸發器
判斷員工是否真正漲工資。
怎麼獲得漲工資之前或者之後的工資 ( for each row )
create or replace trigger emptg1
before update [ of sal ] on emp for each row
declare
begin
if :new.sal = :old.sal < 0 then
raise_application_error(-20002,'不能降工資');
end;
4.實際應用
- 主鍵自增
--建立序列
create sequence seq_userinfo nocycle nocache;
--行級觸發器
create or replace trigger usertg1
before insert on user_info for each row
declare
begin
select seq_userinfo.nextval into :new.uid FROM dual;
end;
- 日誌記錄
--建立序列
create seqence seq_syslog nocycle nocache;
-- 被班級表進行日誌記錄
create or replace trigger gradetg3
after update on grade for each row
DECLARE
v_time varchar(128);
v_msg CLOB;
BEGIN
--賦值當前時間
select to_char(SYSDATE,'yyyy-MM-dd hh24:mm:ss') into v_time from dual;
--插入入職表中
v_msg := '更新,發生在'||V_TIME||',操作前['||:old.g_name||']操作後
['||:new.g_name||']';
insert into sys_log values (seq_syslog.Nextval,v_msg);
dbms_output.put_line('日誌記錄成功!');
END;