Oracle教程第五篇

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Phone_1070333541/article/details/86352422

一、游标

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.异常

  1. oracle 一共定义有2万个错误。
  2. 预定义错误 弹窗 终止程序运行。
  3. 处理异常。
EXCEPTION  
WHEN
	TOO_MANY_ROWS
THEN 
	dbms_output.put_line('返回条数过多,不能赋值');
WHEN
	NO_DATA_FOUND
THEN
	dbms_output.put_line('要查找的数据不存在');
  1. 自定义异常。
-- 如果有员工工资小于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
  1. 其他异常
    所有未拦截的异常都应该被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.工具类

  1. 四个属性
    driver:oracle.jdbc.OracleDriver
    url:jdbc:oracle:thin:@192.168.56.130:1521:orcl
    user:test_302
    password:ORCL
  2. 调用存储过程
//需求:根据某个人的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.分页获取学生数据,包含学生以及班级信息

  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;
  1. 客户端来调用存储过程
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;
  1. 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.触发器的类型

  1. 语句级触发器。
    语句每执行一次就会执行一次。
  2. 行级的触发器。

2.语句级的触发器。

  1. 示例1
// 插入班级信息以后,打印添加成功。
create or replace trigger gradetg1
	after insert on grade
declare 

begin
	dbms_output.put_line("添加成功");
end;

// 自己插入一条信息测试。
  1. 示例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.实际应用

  1. 主键自增
--创建序列
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;
  1. 日志记录
--创建序列
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;

oracle教程第四篇传送门

猜你喜欢

转载自blog.csdn.net/Phone_1070333541/article/details/86352422
今日推荐