Oracle第四天
一、回顾
DDL语言
表空间,用户(权限管理:角色权限(基本权限,开发人员权限,dba)),表(数据类型,修改表,约束)
视图 (view) , 序列(sequence),索引(index),同义词(synonym)
DML语句,事务概念,数据库的备份:导出(exp),导入(imp)
二、学习目标
1. plsql的基本语法
2. 游标&例外
3. 存储过程
4. 存储函数
5. JDBC访问Oracle数据库
6. 触发器
三、plsql的基本语法
/**
过程化语言的基本结构
declare
-- 声明变量
begin
-- 过程化语言
exception
--异常的处理
end;
*/
-- 声明变量
declare
-- 声明普通的变量
i number;
-- 声明变量且赋初始值
j number default 20;
-- 引用类型:引用员工表姓名列的类型
pname emp.ename%type;
pjob emp.job%type;
-- 记录类型: 记录一行的内容,一般对应 *
e_row emp%rowtype;
begin
-- 对变量进行赋值
i := 100;
-- 在控制台打印i的值
dbms_output.put_line(i);
dbms_output.put_line('j =' || j);
-- 把员工的姓名查询出来赋值给变量
select ename,job into pname,pjob from emp where empno = 7654; --
dbms_output.put_line('7788的姓名是:' || pname||',工作是:'|| pjob);
select * into e_row from emp where empno = 7654; --
dbms_output.put_line('7788的姓名是:' || e_row.ename||',工作是:'||e_row.job);
end;
---- 分支语句 (if)
/*
if 条件 then
sql语句;
end if;
*/
-- 输入一个整数,如果大于0打印正数,否则打印非正数
declare
i number;
begin
i := &请输入;
if i > 0 then
dbms_output.put_line('正数');
end if;
if i <= 0 then
dbms_output.put_line('非正数');
end if;
end;
/*
if 条件 then
sql语句;
else
sql语句
end if;
*/
-- 输入一个整数,如果大于0打印正数,否则打印非正数
declare
i number;
begin
i := &请输入;
if i > 0 then
dbms_output.put_line('正数');
else
dbms_output.put_line('非正数');
end if;
end;
/*
if 条件 then
sql语句;
elsif 条件 then
sql语句
elsif 条件 then
sql语句
....
else
sql语句
end if;
*/
-- 输入一个整数,如果大于0打印正数,如果是0,打印零,否则打印负数
declare
i number;
begin
i := &请输入;
if i > 0 then
dbms_output.put_line('正数');
elsif i = 0 then
dbms_output.put_line('零');
else
dbms_output.put_line('负数');
end if;
end;
---- 循环
-- for循环
/*
for 变量 in 游标|1..10 loop
end loop;
*/
-- 打印1-10 十个整数
declare
begin
for i in 1..10 loop
dbms_output.put_line(i);
end loop;
end;
/**
loop循环
loop
循环体;
退出条件;
迭代部分
end loop;
*/
-- 打印1-10 十个整数
declare
i number default 1;
begin
loop
dbms_output.put_line(i);
exit when i = 10;
i := i + 1;
end loop;
end;
/*
while 条件 loop
循环体
迭代
end loop;
*/
-- 打印1-10 十个整数
declare
i number default 1;
begin
while i <= 10 loop
dbms_output.put_line(i);
i := i + 1;
end loop;
end;
四、游标&例外
------ 游标:(集合) ,处理返回多行记录的问题
declare
pname varchar2(20);
begin
-- select into : 只能操作返回一行记录
select ename into pname from emp where deptno = 20;
dbms_output.put_line(pname);
end;
-- 声明游标
--语法: cursor 游标名 is DQL;
-- 遍历游标
/*
1.打开游标, open 游标名;
2.从游标中提取一行的记录:fetch 游标名 into 变量名,...;
3.使用循环, exit when 游标名%notfound;
4.关闭游标, close 游标名;
*/
--- 通过游标的方式打印20号部门的员工姓名
declare
-- 声明游标
cursor cur is select ename,job from emp where deptno = 20;
pname emp.ename%type;
begin
-- 遍历游标
--1.打开游标, open 游标名;
open cur;
loop
--2.从游标中提取一行的记录:fetch 游标名 into 变量名,...;
fetch cur into pname;
exit when cur%notfound; -- 来判断是否提取到了记录
dbms_output.put_line(pname);
--3.使用循环, exit when 游标名%notfound;
end loop;
--4.关闭游标, close 游标名;
close cur;
end;
--- 举例:(使用游标)给20号部门员工涨工资
declare
cursor cur is select empno from emp where deptno = 20;
begin
-- for 循环:自动打开和关闭游标
for i in cur loop
update emp set sal = sal + 1 where empno = i.empno;
end loop;
commit;
end;
--- 例外(异常) :(了解)
declare
pname varchar2(20);
i number ;
begin
i := 10 / 0;
-- select into : 只能操作返回一行记录
select ename into pname from emp where deptno = 20;
dbms_output.put_line(pname);
exception
when no_data_found then
dbms_output.put_line('没有返回记录');
when too_many_rows then
dbms_output.put_line('返回太多的记录');
when value_error then
dbms_output.put_line('赋值类型错误');
when zero_divide then
dbms_output.put_line('除0异常');
when others then
dbms_output.put_line('其他异常');
end;
---- 自定义异常(了解)
--- 游标中没有记录的异常
declare
cursor cur is select ename from emp where deptno = 40;
pname emp.ename%type;
-- 声明异常
not_found exception;
begin
open cur;
fetch cur into pname;
if cur%notfound then
-- 抛出异常
raise not_found;
end if;
exception
when not_found then -- 捕获异常
dbms_output.put_line('没有记录');
end;
五、存储过程
----- 存储过程(dba声明,得授予dba权限): 封装了一组sql语句,提前编译好,效率较高 ,存储在服务端
-- 场景:网购:数据库发生什么改变
-- 库存量-1(update) ,订单增加(insert),钱(update),物流(insert) , 日志(insert)
--- 语法
/*
create [or replace] procedure 存储过程名称(参数名 in|out 类型,....)
as | is
-- 声明变量
begin
-- 过程化语言
end;
*/
--- 举例:给某员工涨工资(打印涨前的工资,和涨后的工资)
create or replace procedure updateSal(eno in number , psal in number)
as
oldsal emp.sal%type;
newsal emp.sal%type;
begin
-- 涨前的工资
select sal into oldsal from emp where empno = eno;
dbms_output.put_line('原工资:' || oldsal);
-- 涨工资
update emp set sal = sal + psal where empno = eno;
-- 涨后的工资
select sal into newsal from emp where empno = eno;
dbms_output.put_line('现工资:' || newsal);
end;
--- 访问存储过程
-- call 存储过程名(参数); -- 只能访问只有输入参数的存储过程
call updateSal(7788, 5);
--- 举例: 查询某员工的年薪
create or replace procedure getYearSal(eno in number , yearsal out number)
is
begin
select sal * 12 + nvl(comm,0) into yearsal from emp where empno = eno;
end;
-- 访问存储过程
declare
yearsal number;
begin
getYearSal(7788 , yearsal);
dbms_output.put_line('年薪:'||yearsal);
end;
--- 举例(返回游标): 获取某部门的所有员工信息
create or replace procedure getEmpsByDeptno(dno in number ,emps out sys_refcursor)
as
begin
open emps for select * from emp where deptno = dno;
end;
--- 访问带有游标的存储过程
declare
emps sys_refcursor;--定义一个游标类型的变量
e_row emp%rowtype;
begin
getemployees(20,emps);
--变量游标
loop
fetch emps into e_row;
exit when emps%notfound;--退出循环的条件
dbms_output.put_line(e_row.empno || e_row.ename);
end loop;
end;
六、存储函数
---- 存储函数:封装了一组sql语句,提前编译好,效率较高 ,存储在服务端
--- 存储函数必须有一个返回值,存储函数可以用select语句中
/**
create or replace function 函数名(参数名 in|out 类型,....)
return type
as | is
begin
return 值;
end;
*/
--- 举例: 查询某员工的年薪
create or replace function getYearSalFun(eno in number)
return number
as
yearsal number;
begin
select sal * 12 + nvl(comm,0) into yearsal from emp where empno = eno;
return yearsal;
end;
--- 访问函数
declare
yearsal number;
begin
yearsal := getYearSalFun(7788);
dbms_output.put_line(yearsal);
end;
-- 在select中访问存储函数
select getYearSalFun(7788) from dual;
七、JDBC访问Oracle数据库
package cn.itcast.test;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import oracle.jdbc.driver.OracleCallableStatement;
import oracle.jdbc.driver.OracleTypes;
public class JDBCTest {
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@192.168.174.129:1521:orcl";
String username = "scott";
String password = "tiger";
Connection conn ;
PreparedStatement pst;
CallableStatement cst;
ResultSet rs;
/**
* 初始化配置信息
*/
@Before
public void init(){
try {
Class.forName(driver);
conn = DriverManager.getConnection(url,username, password);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 存储过程的sql语句语法: {call [(,, ...)]}
* create or replace procedure getYearSal(eno in number , yearsal out number)
* @throws SQLException
*
*/
@Test
public void testProcedure() throws SQLException{
String sql = "{call getYearSal(?,?)}";
//创建CallableStatement对象
cst = conn.prepareCall(sql);
//设置输入参数
cst.setInt(1, 7788);
//注册输出类型
cst.registerOutParameter(2, OracleTypes.NUMBER);
//执行sql语句
cst.execute();
// 得到输出参数的值
int yearsal = cst.getInt(2);
System.out.println(yearsal);
}
/**
* 返回游标的存储过程
* create or replace procedure getEmpsByDeptno(dno in number ,emps out sys_refcursor)
* 存储过程的sql语句语法: {call [(,, ...)]}
* @throws SQLException
*/
@Test
public void testProcedureOutCursor() throws SQLException{
String sql = "{call getEmpsByDeptno(?,?)}";
cst = conn.prepareCall(sql);
cst.setInt(1, 20);
cst.registerOutParameter(2, OracleTypes.CURSOR);
cst.execute();
// OracleCallableStatement ocst = (OracleCallableStatement) cst;
// rs = ocst.getCursor(2);
Object o = cst.getObject(2);
rs = (ResultSet) o;
while(rs.next()){
System.out.println("编号:"+rs.getInt("empno") + ",姓名:"+ rs.getString("ename"));
}
}
/**
* {?= call [(,, ...)]}
*
* create or replace function getYearSalFun(eno in number) return number
* @throws SQLException
*/
@Test
public void testFunction() throws SQLException{
String sql = "{?= call getYearSalFun(?)}";
cst = conn.prepareCall(sql);
cst.registerOutParameter(1, OracleTypes.NUMBER);
cst.setInt(2, 7788);
cst.execute();
int yearsal = cst.getInt(1);
System.out.println(yearsal);
}
/**
* select getYearSalFun(7788) from dual
*
* 传统的方式访问存储函数
* @throws SQLException
*/
@Test
public void testFunction2() throws SQLException {
String sql = "select getYearSalFun(?) yearsal from dual";
pst = conn.prepareStatement(sql);
pst.setInt(1, 7788);
rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("yearsal"));
}
}
@Test
public void test() throws SQLException {
String sql = "select * from emp";
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
while(rs.next()){
System.out.println("编号:"+rs.getInt("empno") + ",姓名:"+ rs.getString("ename"));
}
}
/**
* 关闭资源
* @throws SQLException
*/
@After
public void close() throws SQLException{
if(rs != null){
rs.close();
}
if(pst != null){
pst.close();
}
if(cst != null){
cst.close();
}
if(conn != null){
conn.close();
}
}
}
八、触发器
----- 触发器(监听器):监听表中的数据是否发生了改变
-- 增删改 操作
/*
create or replace trigger 触发器名
after | before 在改变之前还是之后执行触发器
insert | delete | update 监听表的哪个操作
on 表 对哪张表的监听
触发器的级别:表级触发器, 行级触发器
表级触发器不能使用old,new
行级触发器:可以使用old,new
declare
begin
end;
*/
--- 栗子: 添加一条记录,打印添加了一条记录
create or replace trigger insertEmp
after
insert
on emp
declare
begin
dbms_output.put_line('添加了一条记录');
end;
insert into emp (empno ,ename) values(1000,'mike');
--- 栗子:不能给员工降薪
create or replace trigger notUpdateLowerSal
before
update
on emp
for each row -- 行级触发器
declare
begin
if :new.sal < :old.sal then
-- raise_application_error(p1,p2)
-- p1:错误的编号: -20000 -- -20999
-- p2 : 错误的提示信息
raise_application_error(-20000, '不能给员工降薪!!!');
end if;
end;
update emp set sal = sal + 1 where empno = 7788;
--- 触发器小应用 -- 模拟mysql中自增效果
create sequence b_seq;
create or replace trigger auto_incrementTri
before
insert
on emp
for each row
declare
begin
select b_seq.nextval into :new.empno from dual;
end;
insert into emp(ename) values('zhangsan');