基于Oracle 数据库存储过程的创建及调用

PLSQL编程

  • 概念和目的
  1. 什么是PLSQL
    PL/SQL(Procedure Language/SQL)
    PLSQL是Oracle 对sql语言的过程化扩展(类似与Basic)
    指在SQL命令语言中增加了过程处理语句(如分支,循环等),使SQL语言具有过程处理能力。
  2. 程序结构
    通过PLSQL Developer 工具的Test Window 创建程序模板或者通过语句SQL window编写
    提示:PLSQL语言的大小写是不区分的。
    PL/SQL可以分为三 部分:声明部分,可执行部分,异常处理部分。

 Declare  
 -- 声明变量 游标
 I integer
 BEGIN 
 -- 执行语句
 -- 【异常处理】
 END;

其中Declare部分用来声明变量或者游标(结果集类型变量),如果程序中无变量声明可以省略。

  • 输出Hello Word
begin
  -- Test statements here(-- 注释)
  dbms_output.put_line('hello word');
end;

F8 执行结果:
在这里插入图片描述
CMD 命令窗口 模式运行:
:执行结束后并未输出结果,默认情况下,输出选项是关闭状态,我们需要开启一下,set serveroutput on
在这里插入图片描述

  • 变量
    PLSQL编程中常见的变量分两大类:
    1 普通数据类型(char,varchar2,date,number,boolean,long);
    2 特殊变量类型(引用型变量,记录型变量);
    声明变量的方式为:
变量名 变量类型(变量长度) 例如:v_name varchar2(20);

普通型变量
变量赋值的方式有两种:
1 直接赋值语句 :=
v_name varchar2(20) :=‘lisi’;
2 语句赋值,使用select …into…赋值(语法select 值 into 变量)
select ename,sal into v_name,v_sal from emp where empno=7898;
例如:
打印人员信息,包括:姓名,薪水,地址

-- 打印人员信息,包括:姓名,薪水,地址
declare 
  -- Local variables here
  -- 姓名
  v_name varchar2(20) :='lisi';
  -- 薪水
  v_sal number;
  -- 地址
  v_addr varchar2(200);
  i integer;
begin
  -- Test statements here
  dbms_output.put_line('hello word');
  -- 直接赋值
  v_sal :=1500;
  -- 语句赋值
  select '上海昆山' INTO  v_addr from dual;
  -- 输出语句
  dbms_output.put_line('姓名:'||v_name||',薪水:'||v_sal||'地址:'||v_addr); 
end;

在这里插入图片描述
输出结果集:

在这里插入图片描述
引用变量
变量的类型和长度取决于表中字段的类型和长度。
通过表名.列名%TYPE 指定变量的类型和长度.
例如:v_name emp.ename%TYPE;
【实例】 查询emp 表中7839 员工的个人信息,打印姓名和薪水

  • 普通变量必须知道表中的字段类型和长度才能确定变量的长度和类型
-- 查询emp表中7839号员工的个人信息,打印姓名和薪水
declare 
  -- Local variables here
  -- 姓名
  v_name varchar2(20);
  -- 薪水
  v_sal number;
begin
  -- Test statements here

  -- 语句赋值
  select ename,sal into v_name,v_sal from emp where empno=7898;
  -- 输出语句
  dbms_output.put_line('姓名:'||v_name||',薪水:'||v_sal); 
end;

在这里插入图片描述
结果:
在这里插入图片描述
使用引用变量的好处

  • 使用普通变量定义方式,需要知道表中列的类型,而使用引用类型,不需要考虑列的类型,使用%TYPE是非常好的编程风格,因为它使得PL/SQL更加的灵活。更加适应于对数据库定义的更新。
    在这里插入图片描述
-- 查询emp表中7839号员工的个人信息,打印姓名和薪水
declare 
  -- Local variables here
  -- 姓名
  v_name emp.ename%TYPE;
  -- 薪水
  v_sal emp.sal%TYPE;
begin
  -- Test statements here

  -- 语句赋值
  select ename,sal into v_name,v_sal from emp where empno=7898;
  -- 输出语句
  dbms_output.put_line('姓名:'||v_name||',薪水:'||v_sal); 
end;

  • 记录类型变量
    接收表中的一整行记录,相当于Java中的一个对象。
    语法:变量名称 表名%ROWTYPE,
    例如:v_emp emp%rowtype;
    ----查询并打印7898 的姓名和薪水
-- 查询emp表中7898号员工的个人信息,打印姓名和薪水
declare 
  -- Local variables here
  -- 记录型变量
  v_emp emp%ROWTYPE;

begin
  -- Test statements here

  -- 语句赋值
  select * into v_emp from emp where empno=7897;
  -- 输出语句
  dbms_output.put_line('姓名:'||v_emp.ename||',薪水:'||v_emp.sal); 
end;

在这里插入图片描述
---- 结果:
在这里插入图片描述

  • 如果一个表,有100个字段,那么你程序如果要使用者100个字段,使用引用变量一个一个声明很麻烦,记录类型可以方便解决这个问题。
  • 错误的使用:
    1 。记录型变量只能存储一个完整的行数据。
    在这里插入图片描述
    在这里插入图片描述
  • 流程控制
    — 条件分支
BEGIN
	IF 条件1 THEN  执行1
		ELSIF 条件2 THEN 执行2
	ELSE 执行3
	END IF;
END;

注意关键字:ELSIF
— 实例:
判断 emp 表中记录是否超过20条,10-20之间,或者10条以下

-- 条件查询
declare 
  -- Local variables here
  -- 记录型变量
  v_count number;

begin
  -- Test statements here

  -- 语句赋值
  select count(1) into v_count  from emp ;
  -- 输出语句
  if v_count >20 then
    dbms_output.put_line('大于20');
    elsif v_count >10 then
      dbms_output.put_line('大于10小于20');
      else
      dbms_output.put_line('小于10');
    end if;
end;

  • 循环
    在oracle 中有三种循环方式:
    1. LOOP循环(条件成立时退出)
BEGIN
	LOOP 
		EXIT WHEN 退出循序条件
	END_LOOP;
END;

— 【示例】 打印数字1-10

-- loop循环
declare 
  -- Local variables here
  -- 变量
  v_count number;

begin
  -- Test statements here

  -- 语句赋值
  select count(1) into v_count  from emp ;
  -- 输出语句
 loop
    
 exit when v_count<0;
     dbms_output.put_line(v_count);
      v_count :=v_count-1;
 end loop;
end;

结果:
在这里插入图片描述
2.FOR循环用法(1 … 10 表示连续区间)
语法介绍:

BEGIN
	FOR 变量 IN inital_value .. final_value LOOP
		执行语句;
	END LOOP;
END;

下面是控制在一个流程的循环:
初始步骤首先被执行,并且只有一次。这一步可以声明和初始化任何循环控制变量。 接着,condition,initial_value… final_value 进行计算。如果为true,则执行循环体。如果为false,在循环体不执行,只是之后的for循环流量控制跳转到下一条语句。 循环体的执行后,计数器变量的值被增加或减少。 条件现在重新计算。如果为true,循环执行的过程重复(循环体,然后增加步,然后再次条件)。之后条件为false,则FOR-LOOP终止。
以下是PL/SQL for循环的一些特点:
initial_value 和 循环变量 或计算器 final_value 可以是字面值,变量或表达式,但必须计算结果为数字。否则,PL/SQL就会抛出预定义异常VALUE_ERROR。 initial_value不必为1; 但是循环计数器增量(或减量)必须为1。 PL/SQL允许动态确定在运行时的循环范围。

实例:

-- OR循环重复的控制结构,可以有效地编写需要执行的特定次数的循环。
declare 
  -- Local variables here
  -- 声明变量
  v_count number;
begin
  -- Test statements here
  -- 给变量赋值
  select count(1) into v_count from emp ;
  FOR v_count IN 0 .. 5 LOOP
    dbms_output.put_line('数量:'||v_count);
  end loop;
   dbms_output.put_line('数量结束时:'||v_count);  
end;

结果:
在这里插入图片描述

3. While(循环 条件成立时执行)
语法介绍:

BEGIN
	WHILE 条件 LOOP
	 执行语句;
	END LOOP;
END;

实例:

-- While 循环(条件成立时执行)
declare 
  -- Local variables here
  -- 声明变量
  v_count number;
begin
  -- Test statements here
  -- 给变量赋值
  select count(1) into v_count from emp ;
  while v_count >0 loop
    dbms_output.put_line('数量:'||v_count);
    v_count :=v_count-1;
    end loop;
    dbms_output.put_line('数量结束时:'||v_count);
end;

结果:
在这里插入图片描述

  • 游标
    什么是游标:
    用于临时存储一个查询返回的多行数据(结果集,类似于java 的JDBC连接返回的ResultSet集合),通过遍历游标,可以逐行访问处理该结果集的数据。

游标的使用方式:声明–> 打开—>读取—>关闭。


语法:

  1. 游标声明:
    CURSOR 游标名[(参数列表)] IS 查询语句;
  2. 游标打开:
    OPEN 游标名;
  3. 游标取值:
    FETCH 游标名 INTO 变量列表;
  4. 游标关闭:
    CLOSE 游标名;

游标的属性

游标的属性 返回值类型 说明
%ROWCOUNT 整型 获得FETCH 语句返回的数据行数
%FOUND 布尔型 最近的FETCH语句返回一行的数据,则为真,否则为假
%NOTFOUND 布尔型 与%FOUND属性返回值相反
%ISOPEN 布尔型 游标已经打开时的值为真,否则为假。

其中**%NOTFOUND** 是在游标中找不到元素的时候返回TRUE..通常用来判断退出循环。

  • 创建和使用
    【实例】使用游标查询emp 表中所有的员工的姓名和薪水,并依次打印出来。
-- 使用游标查询emp 表中所有的员工的姓名和薪水,并依次打印出来。
declare 
  -- Local variables here
  -- 声明游标
  cursor c_emp is select ename,sal from emp;
  -- 声明变量接收游标中的数据
  v_name emp.ename%type;
  v_sal emp.sal%type;
 
begin
  -- Test statements here
  -- 打开游标
  OPEN c_emp;
  -- 遍历循环
  loop 
   
  -- 获取游标中的数据
  FETCH c_emp into v_name,v_sal;
  -- 退出循环 
  exit when c_emp%notfound;
  -- 输出
  dbms_output.put_line('ename:'||v_name||'sal:'||v_sal);
  
  end loop;
  -- 关闭游标  
  close c_emp;
end;

在这里插入图片描述


带参数的游标、
【示例:】使用游标查询并打印某部门的员工的姓名和薪资,部门编号为运行时输入

-- 使用游标查询并打印某部门的员工的姓名和薪资,部门编号为运行时输入
declare 
  -- Local variables here
  -- 声明游标
  cursor c_emp (v_empno emp.empno%type)is select ename,sal from emp where empno =v_empno;
  -- 声明变量接收游标中的数据
  v_name emp.ename%type;
  v_sal emp.sal%type;
 
begin
  -- Test statements here
  -- 打开游标
  OPEN c_emp(7898);
  -- 遍历循环
  loop 
   
  -- 获取游标中的数据
  FETCH c_emp into v_name,v_sal;
  -- 退出循环 
  exit when c_emp%notfound;
  -- 输出
  dbms_output.put_line('ename:'||v_name||'sal:'||v_sal);
  
  end loop;
  -- 关闭游标  
  close c_emp;
end;

在这里插入图片描述
执行结果:
在这里插入图片描述


  • 存储过程:
    概念作用
    前面编写的PLSQL程序可以进行表操作,判断,循环逻辑处理的工作,但无法重复调用,可以说前面的代码全部编写在main方法中,是匿名程序,JAVA 可以通过封装对象和方法来解决复用问题,PLSQL 是将一个个PLSQL的业务处理过程储存起来进行复用,这些被存储起来的PLSQL程序称之为存储过程。
    存储过程的作用
  1. 在开发程序中,为了一个特定的业务功能,会向数据库进行多次链接和关闭链接是很耗费资源的,需要对数据库进行多次的I/O读写,性能比较低,如果把这些业务放到PLSQL中,在应用程序中只需要调用PLSQL 就可以做到连接关闭一次,数据库就可以实现我们的业务,可以大大提高效率。
  2. ORACLE官方给的建议:能够让数据库操作的程序不要放在程序中,在数据库中实现基本上不会出现错误,在程序中操作可能会存在错误,(如果在数据库中操作数据,可以有一定的日志恢复等功能)。
    语法:
CREATE OR REPLACE PROCEDURE 过程名称[(参数列表)] IS
BEGIN

END [过程名称];

根据参数的类型分为三类:
在这里插入图片描述
在这里插入图片描述
1。 无参

-- 编写存储过程
create or replace procedure p_emp is
-- 声明变量
begin
  dbms_output.put_line('Hello Word');
end p_emp;

运行之后再PLSQL中调用存储过程

-- PLSQL调用存储过程
declare 
  
 
begin
  -- PLSQL 调用存储过程
 p_emp;
end;

执行结果:
在这里插入图片描述
在这里插入图片描述
注意:
第一个问题:is和as是可以互用的,用哪个都没有关系。
第二个问题:过程中没有declare关键字,declare用在语句块中。

2。带输入参数的存储过程
【示例】查询并打印某个员工(如7989号员工)的姓名和薪水–存储过程,要求调用的时候传入员工的编号,自动控制台打印。

-- 查询并打印某个员工(如7989号员工)的姓名和薪水
--存储过程,要求调用的时候传入员工的编号,自动控制台打印。
create or replace procedure p_emp ( in_empno IN emp.empno%type )is
-- 声明变量
v_name emp.ename%type;
v_sal emp.sal%type;

begin
  --查询赋值
  
  select ename,sal into v_name,v_sal from emp where empno=in_empno;
  dbms_output.put_line('v_name:'||v_name||'v_sal:'||v_sal);
end p_emp;

在这里插入图片描述
调用存储过程:

-- 查询并打印某个员工(如7989号员工)的姓名和薪水--存储过程,
-- 要求调用的时候传入员工的编号,自动控制台打印。
declare 
  
 
begin
  -- PLSQL 调用存储过程
 p_emp(7898);
end;

结果:
在这里插入图片描述
命令窗口调用:
在这里插入图片描述
3。带输入输出参数(返回值)的。
【示例】输入员工号查询某个员工信息,要求,将薪水作为返回值输出,给调用的程序使用。

-- 输入员工号查询某个员工信息,要求,
-- 将薪水作为返回值输出,给调用的程序使用。工的编号,自动控制台打印。
create or replace procedure p_emp ( in_empno IN emp.empno%type,o_sal out emp.sal%type)is

begin
  --查询赋值
  
  select sal into o_sal from emp where empno=in_empno;
  
  
end p_emp;

调用存储过程

-- 输入员工号查询某个员工信息,要求
-- 将薪水作为返回值输出,给调用的程序使用。。
declare 
  -- 声明变量用来接收参数。。。
 v_sal emp.sal%type;
begin
  -- PLSQL 调用存储过程
   p_emp(7898,v_sal);
 dbms_output.put_line(v_sal);
end;

在这里插入图片描述

JAVA中调用存储过程

如果一条语句无法实现结果集,比如需要多表查询,或者需要复杂逻辑查询,我们可以调用存储查询出你的结果;


分析JDK.API

CallableStatement 				prepareCall(String sql);
//创建一个调用数据库存储过程的CallableStatement 	对象。

调用步骤:

  1. 加载驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
  1. 获取连接对象
String url="jdbc:oracle:thin:@localhost:1521:orcl";
        String username="root";
        String password="root";
        Connection connection = DriverManager.getConnection(url, username, password);
  1. 获取语句对象
String sql="{call p_emp(?,?)}";
CallableStatement call = connection.prepareCall(sql);
  1. 设置输出参数
 call.setInt(1, 7898);
  1. 注册输出参数
call.registerOutParameter(2, OracleTypes.DOUBLE);
  1. 执行存储过程
call.execute();
  1. 获取输出参数
 double sal = call.getDouble(2);
 System.out.println(sal);
  1. 释放资源
call.close();
connection.close();

总体测试方法:

  public static void main(String[] args) throws Exception {
        //1.加载驱动
        Class.forName("oracle.jdbc.driver.OracleDriver");

        //2.获取连接对象
        String url="jdbc:oracle:thin:@localhost:1521:orcl";
        String username="root";
        String password="root";
        Connection connection = DriverManager.getConnection(url, username, password);

        //3.获取语句对象
        String sql="{call p_emp(?,?)}";
        CallableStatement call = connection.prepareCall(sql);
        //4.设置输入参数
        call.setInt(1, 7898);
        //5.注册输出参数
        call.registerOutParameter(2, OracleTypes.DOUBLE);
        //6.执行存储过程
        call.execute();
        //7.获取输出参数
        double sal = call.getDouble(2);
        System.out.println(sal);

        //8.释放资源
        call.close();
        connection.close();


    }
  • 一般的数据库读取测试
 @Test
    public void oracleConnectTest() throws  Exception{
        //1.加载驱动
        Class.forName("oracle.jdbc.driver.OracleDriver");
        //2.获取连接
        String  url="dbc:oracle:thin:@localhost:1521:orcl";
        String password="root";
        String username="root";
        Connection connection = DriverManager.getConnection(url, username, password);
        //3.获取传输器
        String sql="select * from emp";
        PreparedStatement statement = connection.prepareStatement(sql);
        //4.设置参数

        //5.执行sql语句
       statement.execute();
       //6.获取结果集
        ResultSet resultSet = statement.executeQuery();
        //7.遍历结果集
        while (resultSet.next()){
            People people= new People();
            people.setNumber(resultSet.getInt("empno"));
            people.setName(resultSet.getString("ename"));
            people.setSal(resultSet.getDouble("sal"));
            System.out.println(people);
        }
        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }

POJO类:

 class People{
        private Integer number;
        private String name;
        private Double sal;

        public Integer getNumber() {
            return number;
        }

        public void setNumber(Integer number) {
            this.number = number;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Double getSal() {
            return sal;
        }

        public void setSal(Double sal) {
            this.sal = sal;
        }

        @Override
        public String toString() {
            return "people{" +
                    "number=" + number +
                    ", name='" + name + '\'' +
                    ", sal=" + sal +
                    '}';
        }
    }

执行结果:
在这里插入图片描述

  • java包资源:ojdbc14.jar

链接:https://pan.baidu.com/s/1mBgRKpBh3ZeVQL7wIj8kOA
提取码:tqiv

猜你喜欢

转载自blog.csdn.net/qq_16183731/article/details/84675688