Oracle-PL/SQL编程

Oracle PL/SQL 编程 PL/SQL(Procedural Language/SQL)是 Oracle 数据库的过程化扩展,允许在 SQL 之外使用控制流、异常处理和游标等结构。PL/SQL 集成了 SQL 的功能,并增加了编程语言的特性,使得开发人员可以在数据库中实现复杂的业务逻辑。PL/SQL 是 Oracle 数据库中重要的编程语言,通过其强大的控制结构、异常处理机制和游标功能,开发者可以高效地实现复杂的数据库逻辑和批处理任务。 阅读详情

在线练习oracle的sql语句

https://livesql.oracle.com
livesql各个功能的介绍

数据准备

-- 准备数据
create table dept(  
  deptno     number(2,0),  
  dname      varchar2(14),  
  loc        varchar2(13),  
  constraint pk_dept primary key (deptno)  
);
create table emp(  
  empno    number(4,0),  
  ename    varchar2(10),  
  job      varchar2(9),  
  mgr      number(4,0),  
  hiredate date,  
  sal      number(7,2),  
  comm     number(7,2),  
  deptno   number(2,0),  
  constraint pk_emp primary key (empno),  
  constraint fk_deptno foreign key (deptno) references dept (deptno)  
);
insert into DEPT (DEPTNO, DNAME, LOC)
values(10, 'ACCOUNTING', 'NEW YORK');

insert into dept  
values(20, 'RESEARCH', 'DALLAS');

insert into dept  
values(30, 'SALES', 'CHICAGO');

insert into dept  
values(40, 'OPERATIONS', 'BOSTON');

insert into emp  
values(  
 7839, 'KING', 'PRESIDENT', null,  
 to_date('17-11-1981','dd-mm-yyyy'),  
 5000, null, 10  
);

insert into emp  
values(  
 7698, 'BLAKE', 'MANAGER', 7839,  
 to_date('1-5-1981','dd-mm-yyyy'),  
 2850, null, 30  
);

insert into emp  
values(  
 7782, 'CLARK', 'MANAGER', 7839,  
 to_date('9-6-1981','dd-mm-yyyy'),  
 2450, null, 10  
);

insert into emp  
values(  
 7566, 'JONES', 'MANAGER', 7839,  
 to_date('2-4-1981','dd-mm-yyyy'),  
 2975, null, 20  
);

insert into emp  
values(  
 7788, 'SCOTT', 'ANALYST', 7566,  
 to_date('13-JUL-87','dd-mm-rr') - 85,  
 3000, null, 20  
);

insert into emp  
values(  
 7902, 'FORD', 'ANALYST', 7566,  
 to_date('3-12-1981','dd-mm-yyyy'),  
 3000, null, 20  
);

insert into emp  
values(  
 7369, 'SMITH', 'CLERK', 7902,  
 to_date('17-12-1980','dd-mm-yyyy'),  
 800, null, 20  
);

insert into emp  
values(  
 7499, 'ALLEN', 'SALESMAN', 7698,  
 to_date('20-2-1981','dd-mm-yyyy'),  
 1600, 300, 30  
);

insert into emp  
values(  
 7521, 'WARD', 'SALESMAN', 7698,  
 to_date('22-2-1981','dd-mm-yyyy'),  
 1250, 500, 30  
);

insert into emp  
values(  
 7654, 'MARTIN', 'SALESMAN', 7698,  
 to_date('28-9-1981','dd-mm-yyyy'),  
 1250, 1400, 30  
);

insert into emp  
values(  
 7844, 'TURNER', 'SALESMAN', 7698,  
 to_date('8-9-1981','dd-mm-yyyy'),  
 1500, 0, 30  
);

insert into emp  
values(  
 7876, 'ADAMS', 'CLERK', 7788,  
 to_date('13-JUL-87', 'dd-mm-rr') - 51,  
 1100, null, 20  
);

insert into emp  
values(  
 7900, 'JAMES', 'CLERK', 7698,  
 to_date('3-12-1981','dd-mm-yyyy'),  
 950, null, 30  
);

insert into emp  
values(  
 7934, 'MILLER', 'CLERK', 7782,  
 to_date('23-1-1982','dd-mm-yyyy'),  
 1300, null, 10  
);

pl/sql的介绍

什么是PL/SQL?

  1. PL/SQL(Procedure Language/SQL)
  2. PLSQL是Oracle对sql语言的过程化扩展 (类似于Basic)
  3. 指在SQL命令语言中增加了过程处理语句(如分支、循环等),使SQL语言具有过程处理能力。过程、函数可以在java程序中调用
  4. 过程、函数、触发器是pl/sql编写
  5. 过程、函数、触发器是在oracle中

优点

  1. 通过存储过程,减少程序的编译,提供应用程序的运行性能
  2. 模块化的设计思想[分页的过程,订单的过程,转账的过程…]
  3. 减少网络传输量
  4. 提高安全性

缺点

移植性不好

分类

  • 存储过程
  • 函数
  • 触发器
  • 包

pl/sql开发工具

  • sqlplus开发工具
    sqlplus是oracle公司提供的一个工具,
  • pl/sql developer开发工具
    pl/sql developer是用于开发pl/sql块的集成开发环境(ide),它是一个独立的产品,而不是oracle的一个附带品

编写规范

-- 注释
-- 单行注释
select * from emp where empno=7788;
/*多行注释*/
-- 标识符号的命名规范
-- 当定义变量时,建议使用v_作为前缀 v_sal
-- 当定义常量时,建议使用c_作为前缀c_rate
-- 当定义游标时,建议使用_cursor作为后缀emp_cursor;
-- 当定义异常时,建议使用e_作为前缀e_error

PLSQL编程

程序结构

通过Plsql Developer工具的Test Window 创建 程序模版或者通过语句在SQL Window编写

提示:PLSQL语言的大小写是不区分的

PL/SQL可以分为三个部分:声明部分、可执行部分、异常处理部分。

declare
-- 声明变量、游标
i integer;
begin
 -- 执行语句
 
-- 异常处理
end;

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

declare
-- 定义部分:定义常量、变量、游标、异常、复杂数据类型
begin
-- 执行部分【该部分是可选的】
exception 
-- 异常处理部门,处理运行的各种错误【该部分是可选的】
end;

Hello World

-- 打印Hello World
begin
/*
dbms_output 为oracle内置程序包,
dbms_output相当于java中的System.out
put_line相当于java中的System.out下的println()方法
*/
dbms_output.put_line('Hello World');
end;

在PL/SQL Developer中输出
在这里插入图片描述
在sqlplus中也可以编写运行PLSQL程序:
在oracle命令行中,sql语句结尾加上/表示立即执行该语句。
执行结束后并未显示输出的结果,默认情况下,输出选项是关闭状态的 我们需要开启一下 set serveroutput on
在这里插入图片描述

变量

PLSQL编程中常见的变量分两大类:

  1. 普通数据类型(char,varchar2, date, number, boolean, long)
  2. 特殊变量类型(引用型变量、记录型变量)
/*
变量
声明变量的方式为:
变量名  变量类型(变量长度)  例如: v_name  varchar2(20);

*/
declare 
-- 定义变量 v_ename
v_ename varchar2(5);
begin
-- 执行部分
-- &empno接收从控制台输入的变量empno
select ename into v_ename from emp where empno=&empno;
-- dbms_output..put_line:控制台输出
-- 字符串连接符 ||
dbms_output.put_line('用户名是' || v_ename);
end;

执行sql语句后
在这里插入图片描述
在这里插入图片描述

1.4.1. 普通变量

变量赋值的方式有两种:

  1. 直接赋值语句 := 比如: v_name := 'zhangsan'
  2. 语句赋值,使用select …into … 赋值:(语法 select 值 into 变量)
/*
打印人员个人信息,包括: 姓名、薪水、地址
*/
declare
-- 姓名
v_name varchar2(20) :='张三';--声明变量的同时直接赋值
-- 薪水
v_sal number;
-- 地址
v_addr varchar2(200);
begin
--- 在程序中直接赋值
v_sal :=1500;
-- 语句赋值
select '北京市朝阳区' into v_addr from dual;
-- 打印变量
dbms_output.put_line('姓名:'||v_name||',薪水:'||v_sal||',地址:'||v_addr);
end;

在这里插入图片描述

1.4.2. 引用型变量

变量的类型和长度取决于表中字段的类型和长度
通过表名.列名%TYPE指定变量的类型和长度,例如: v_name emp.ename%TYPE;

/*
查询emp表中7839号员工的个人信息,打印姓名和薪水
*/
declare
-- 姓名
v_name emp.ename%type;--声明变量直接赋值
-- 薪水
v_sal emp.sal%type;
begin
--  查询表中的姓名和薪水并赋值给变量
-- 注意查询的字段和赋值的变量的顺序、个数、类型要一致
select ename,sal into v_name,v_sal from  emp where empno=7839;

-- 打印变量
dbms_output.put_line('姓名:'||v_name||',薪水:'||v_sal);
end;

在这里插入图片描述
引用型变量的好处:
使用普通变量定义方式,需要知道表中列的类型,而使用引用类型,不需要考虑列的类型,使用%TYPE是非常好的编程风格,因为它使得PL/SQL更加灵活,更加适应于对数据库定义的更新。

1.4.3. 记录型变量

接受表中的一整行记录,相当于Java中的一个对象
语法: 变量名称 表名%ROWTYPE, 例如: v_emp emp%rowtype;

/*
记录型变量
语法: 变量名称   表名%ROWTYPE,
例如: v_emp emp%rowtype;
*/
-- 查询emp表中7839号员工的个人信息,打印姓名和薪水
declare 
-- 记录型变量接受一行
v_emp emp%rowtype;
begin
-- 记录型变量默认接受表中的一行数据,不能指定字段。
select * into v_emp from emp where empno=7839;
-- 打印变量,通过变量名.属性的方式获取变量中的值
dbms_output.put_line('姓名'||v_emp.ename||',薪水:'||v_emp.sal);
end;

执行sql语句输出
在这里插入图片描述

如果有一个表,有100个字段,那么你程序如果要使用这100字段话,如果你使用引用型变量一个个声明,会特别麻烦,记录型变量可以方便的解决这个问题

错误的使用:
1. 记录型变量只能存储一个完整的行数据
在这里插入图片描述
2.返回的行太多了,记录型变量也接收不了
在这里插入图片描述

流程控制

条件分支

/*
条件分支
语法:
*/
begin
	if 条件1 then 执行1
		elsif 条件2 then 执行2
		else 执行3
	end if;
end;

注意关键字:elsif

/*
判断emp表中记录是否超过20条,10-20之间,或者10条以下
*/
declare
-- 声明变量接受emp表中的记录数
v_count number;
begin
-- 查询emp表中的记录数赋值给变量
select count(1) into v_count from emp;
-- 判断打印
  if v_count >20 then
     dbms_output.put_line('EMP表中的记录数超过了20条为:'||v_count||'条。');
  elsif v_count >=10 then
     dbms_output.put_line('EMP表中的记录数在10~20条之间为:'||v_count||'条。');  
  else 
     dbms_output.put_line('EMP表中的记录数在10条以下为:'||v_count||'条。');     
  end if;
end;   

循环

在ORACLE中有三种循环方式,这里我们不展开,只介绍其中一种:loop循环

-- 循环
-- 语法:
begin
-- 开始循环
loop
exit when 退出循环条件
-- 结束循环
end loop;
end;
-- 打印数字1-10
declare 
-- 声明循环变量并赋初值
v_num number :=1;
begin
loop
exit when v_num >10;
dbms_output.put_line(v_num);
-- 循环变量自增
      v_num :=v_num+1;
      end loop;
end;

在这里插入图片描述
while循环

-- 创建表
create table users(
username varchar2(50),
pwd varchar2(50)
);
/*
请编写一个过程,可输入用户名,
并循环添加10个用户到users表中,用户编号从11开始增加
*/

create or replace procedure sp_pro6(spName varchar2) is 
-- 定义变量v_num,类型为number,等于11
v_num number :=11;
-- 执行
begin
    while v_num <=20 loop
    -- 循环语句
    insert into users values(v_num,spName);
    -- 自增  
    v_num := v_num+1;
    end loop; 
end;
-- 调用存储过程
call sp_pro6('子正');

或者这样调用存储过程

-- 调用存储过程
exec sp_pro6('子正');

或者这样调用存储过程

begin
sp_pro6('子正');
end;
select * from users;

查询调用存储过程结果插入的数据
在这里插入图片描述
for循环

/*
循环语句
for循环
基本for循环的结构如下
begin
     for i in reverse 1.. 10 loop
     insert into users values(i,'紫狐');
     end loop;
end;     
*/

begin
-- 控制变量i,在隐含中就不停的增加
  for i in reverse 1 .. 10 loop
  insert into users values (i,'紫狐');
  end loop;
end;

执行结果如下:
在这里插入图片描述
不建议使用,
顺序控制语句goto,null

/*
顺序控制语句 
1. goto语句
goto语句用于跳转到特定标号去执行语句,
注意由于使用goto语句会增加程序的复杂性,
并使得应用程序可读性变差,所以在做开发时,
不建议使用goto语句
基本语法如下 goto label,其中label是已经定义好的标号名
*/
declare
i int :=1;
begin
     loop
     dbms_output.put_line('输出i='||i);
     if i =10 then
     goto end_loop;
     end if;
     i :=i+1;
     end loop;
     <<end_loop>>
     dbms_output.put_line('循环结束');
     end;

控制台输出
在这里插入图片描述

/*
顺序控制语句 
1. null语句
null语句不会执行任何操作,并且会直接将控制传递到下一条语句
使用null语句的主要好处是可以提高pl/sql的可读性
*/
declare
       v_sal emp.sal%type;
       v_ename emp.ename%type;
begin
-- &no 接口从控制台输入的变量
     select ename,sal into v_ename,v_sal from emp where empno=&no;       
     if v_sal<3000 then
        update emp set comm=sal*0.1 where ename=v_ename;
     else
         null;
     end if; 
end; 

执行sql语句后,弹出以下界面
在这里插入图片描述

游标

什么是游标

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

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

游标声明:

CURSOR 游标名[(参数列表)] IS 查询语句;

游标的打开:

OPEN 游标名;

游标的取值:

FETCH 游标名 INTO 变量列表;

游标的关闭:

CLOSE 游标名;

游标的属性

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

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

创建和使用

-- 使用游标查询emp表中所有员工的姓名和工资,并将其依次打印出来。
declare
       cursor c_emp is select ename,sal from emp;
       --声明变量用来接受游标中的元素
       v_ename emp.ename%type;
       v_sal emp.sal%type;
begin
     --打开游标
     open c_emp;
     --遍历游标中的值
     loop
     -- 通过fetch语句获取游标中的值并赋值给变量
     fetch c_emp into v_ename,v_sal;
     -- 通过%notfound判断是否有值,有值打印,无则退出循环
     exit when c_emp%notfound;
     dbms_output.put_line('姓名'||v_ename||',薪水:'||v_sal);
     end loop;
     -- 关闭游标
     close c_emp;
end;     

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

带参数的游标

-- 使用游标查询并打印某部门的员工的姓名和薪资,部门编号为运行时手动输入。
declare
-- 声明游标传递参数
   cursor c_emp(v_empno emp.empno%type) is
   select ename,sal from emp where empno=v_empno;
       --声明变量用来接受游标中的元素
       v_ename emp.ename%type;
       v_sal emp.sal%type;
begin
     --打开游标传递参数
     open c_emp(7982);
      --遍历游标中的值
      loop
     -- 通过%NOTFOUND判断是否有值,有值打印,没有则退出循环
     exit when c_emp%notfound;
     -- 通过fetch语句获取游标中的值并赋值给变量
     fetch c_emp into v_ename,v_sal;
     dbms_output.put_line('姓名'||v_ename||',薪水:'||v_sal);
     end loop;
     -- 关闭游标
     close c_emp;
end;     

注意:%NOTFOUND属性默认值为FLASE,所以在循环中要注意判断条件的位置.如果先判断在FETCH会导致最后一条记录的值被打印两次(多循环一次默认);

存储过程

概念作用

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

语法

create or replace procedure 过程名称[(参数列表)] is
变量名 变量类型
begin
end [过程名称];

根据参数的类型,我们将其分为3类讲解:

  • 不带参数的

  • 带输入参数的

  • 带输入输出参数(返回值)的。

无参存储

通过Plsql Developer或者语句创建存储过程:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

-- 通过调用存储过程打印hello world
-- 创建存储过程
create or replace procedure pro6
is
begin
dbms_output.put_line('hello world');  
end pro6;

通过工具查看创建好的存储过程:
在这里插入图片描述

调用存储过程

  1. 通过PLSQL程序调用:
begin
--- 直接输入调用存储过程的名称
pro6;
end;

PLSQL调用存储过程,执行结果如下:
在这里插入图片描述

在SQLPLUS中通过EXEC命令调用:
在这里插入图片描述
提示:SQLPLUS中显示结果的前提是需要 set serveroutput on
注意:
第一个问题:is和as是可以互用的,用哪个都没关系的
第二个问题:过程中没有declare关键字,declare用在语句块中

带输入参数的存储过程

-- 查询并打印某个员工(如7839号员工)的姓名和薪水--存储过程:要求,调用的时候传入员工编号,自动控制台打印。
-- 要求,调用的时候传入员工编号,自动控制台打印。
-- in:表示输入参数,存储过程中,
-- 存储过程名称后的阔后里面的 参数后面有个默认的in,表示是输入参数
create or replace procedure P_QUERYNAMEANDSAL(I_EMPNO  in EMP.EMPNO%TYPE)
is 
-- 声明变量接受查询结果
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
-- 根据用户传递的员工号查询姓名和薪水
select  ename,sal into v_ename,v_sal from emp where empno=I_EMPNO;
-- 打印结果
   dbms_output.put_line('姓名'||v_ename||',薪水:'||v_sal);
end P_QUERYNAMEANDSAL;

命令调用:

SQL> exec p_querynameandsal(7839);
姓名:KING,薪水:5000

PL/SQL 过程已成功完成。

PLSQL程序调用:

begin

 P_QUERYNAMEANDSAL(7839);

end;

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

带输出参数的存储过程

-- 输入员工号查询某个员工(7839号员工)信息,要求,将薪水作为返回值输出,给调用的程序使用。
-- out:表示是输出参数
create or replace procedure P_QUERYSAL_OUT(I_EMPNO in emp.empno%type,O_SAL out emp.sal%type)
is
begin
select sal into O_SAL from emp where empno=I_EMPNO;
end P_QUERYSAL_OUT;

PLSQL程序调用:

-- 调用存储过程
declare
       -- 声明一个变量接受存储过程的输出参数
       v_sal emp.sal%type;
   begin
        P_QUERYSAL_OUT(7839,v_sal);--注意参数的顺序
        dbms_output.put_line(v_sal);
   end;

注意:调用的时候,参数要与定义的参数的顺序和类型一致.
执行存储过程,输出结果
在这里插入图片描述

3.7. JAVA程序调用存储过程

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

3.7.1. 分析jdk API

在这里插入图片描述
通过Connection对象的prepareCall方法可以调用存储过程
在这里插入图片描述
得出结论: 通过Connection对象调用prepareCall方法传递一个转义sql语句调用存储过程, 输入参数直接调用set方法传递.输出参数需要注册后,执行存储过程,通过get方法获取.参数列表的下标是从1开始的

实现代码

准备环境:

  • 导入Oracle的jar包
 <!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc10 -->
        <dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>19.14.0.0</version>
        </dependency>
  • 创建存储过程
create or replace procedure p_querysal(I_EMPNO in emp.empno%type,O_SAL out emp.sal%type)
is
begin
select sal into O_SAL from emp where empno=I_EMPNO;
end p_querysal;

java程序调用oracle存储过程

import oracle.jdbc.OracleTypes;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;

/**
 * @author 16837
 */
@SuppressWarnings("{all}")
public class TestCaseDemo {
    private static String driver = "oracle.jdbc.OracleDriver";

    private static String url = "jdbc:oracle:thin:@localhost:1521:XE";

    private static String user = "sys as sysdba";

    private static String password = "sys";

    public static void main(String[] args) throws Exception {
        Connection connection = DriverManager.getConnection(url, user, password);
        /*获取语句对象*/
        /*call 存储过程(参数列表)*/
        String sql = "{ call p_querysal(?,?) }";
        CallableStatement callableStatement = connection.prepareCall(sql);
        /*设置输入参数*/
        callableStatement.setInt(1, 7839);
        /*注册输出参数*/
        callableStatement.registerOutParameter(2, OracleTypes.DOUBLE);
        /*执行存储过程*/
        callableStatement.execute();
        /*获取输出参数*/
        double sal = callableStatement.getDouble(2);
        System.out.println("薪水:" + sal);
        /*释放资源*/
        callableStatement.close();
        connection.close();
    }
}

控制台输出:
在这里插入图片描述

有返回值的存储过程

 /*
    有返回值的存储过程(非列表)
    编写一个存储过程,可以输入雇员的编号,返回该雇员的姓名
  */
  -- 有输入和输出的存储过程
  create or replace procedure sp_pro8(spno in number, spName out varchar) is
begin
  select ename into spNae from emp where empno = &no;
end;

由于oracle存储过程没有返回值,它的所有返回值都是通过out参数来替代的,列表同样也不例外,但由于是集合,所以不能用一般的参数,必须要用package了,所以要分两部分:

  • 建立一个包,如下
-- 返回结果集的过程
-- 创建一个包,在该包中,我定义类型test_cursor,是个游标
create or replace package testpackage as
-- ref cursor 游标
type test_cursor is ref cursor;
end testpackage;
  • 建立存储过程,如下:
-- 创建存储过程
create or replace procedure sp_pro9(spNo in number,
           p_cursor out testpackage.test_cursor) 
 is
begin
  open p_cursor for
    select * from emp where deptno = spNo;
end;
  • 在Java程序中调用
package com.study;

import oracle.jdbc.OracleTypes;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

@SuppressWarnings({"all"})
public class TestCase {
    private static String driver = "oracle.jdbc.OracleDriver";

    private static String url = "jdbc:oracle:thin:@localhost:1521:XE";

    private static String user = "sys as sysdba";

    private static String password = "sys";

    public static void main(String[] args) {
        try {
            /*加载驱动*/
            Class.forName(driver);
            /*得到连接*/
            Connection connection = DriverManager.getConnection(url, user, password);
            CallableStatement cs = connection.prepareCall("{call sp_pro9(?,?)}");
            /*给?赋值*/
            cs.setInt(1, 10);
            /*注册输出参数类型为oracle的游标*/
            /*oracle.jdbc.OracleTypes.CURSOR*/
            cs.registerOutParameter(2, OracleTypes.CURSOR);
            /*调用执行存储过程*/
            cs.execute();
            /*得到结果集*/
            ResultSet rs = (ResultSet) cs.getObject(2);
            while (rs.next()) {
                System.out.println(rs.getInt(1) + " " + rs.getString(2));
            }
            /*关闭连接*/
            cs.close();
            rs.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

java程序调用之后,控制台输出如下:
在这里插入图片描述

函数

函数返回的是一个值

/*
函数:
函数用于返回特定的数据,
当建立函数时,在函数头部必须包含return子句,
而在函数体内必须包含return 语句返回的数据,
可以使用create function 来建立函数,
实际案例如下:
*/
create or replace function annual_income(v_name varchar2)
return number is 
annual_salary number(7,2);
begin
     select sal*12+nvl(comm,0) into annual_salary from emp where ename=v_name;
     return annual_salary;
end;
-- 调用函数
declare 
v_income number;
begin
v_income :=sys.annual_income('SMITH');
dbms_output.put_line('SMITH的收入:'||v_income);
end;

执行sql,输出结果如下:
在这里插入图片描述

-- 删除函数
drop function sys.annual_income;

包

包用于在逻辑上组合过程和函数,它由包规范和包体两部分组成。
我们可以使用create package命令来创建包
实例:

-- 创建包
-- 创建一个包sp_package
-- 声明该包有一个存储过程update_sal
-- 声明该包有一个函数annual_income
create or replace package sp_package is 
procedure update_sal(newname varchar2,newsal number);
function annual_income(ename varchar2) return number;
end sp_package;
-- 删除包
drop package sp_package;

在这里插入图片描述
包的规范只包含了过程和函数的说明,但是没有过程和函数的实现代码。包用于实现包规范中的过程和函数
建立包体可以使用create package body命令

-- 建立包体可以使用create package body命令
create or replace package body sp_package is
  procedure update_sal(newname varchar2, newsal number) is
  begin
    update emp set sal = newsal where ename = newname;
  end;
  function annual_income(ename varchar2) return number is
    annual_salary number;
  begin
    select sal * 12 + nvl(comm, 0)
      into annual_salary
      from emp
     where ename = newname;
    return annual_salary;
  end;

end;

调用包的过程或是函数

当调用包的过程或是函数时,在过程和函数前需要带有包名

-- 例如:
call sp_package.update_sal('SMITH');

触发器

/*
  触发器
  触发器时指隐含的执行的存储过程。当定义触发器时,必须要指定触发的事件和触发的操作,
  常用的触发事件包括insert、update、delete语句,而触发操作实际就是一个pl/sql块。可以
  使用create trigger来建立触发器
  触发器可以维护数据库的安全性和一致性
*/

变量和常量

在编写pl/sq程序时,可以定义变量和常量;在pl/sql程序中包括有:

  • 标量类型(scalar)
  • 复合类型(composite)
  • 参照类型(reference)
  • lob (large object)

标量类型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

declare
  c_tax_rate number(3, 2) := 0.03;

  -- 用户名
  v_ename   varchar2(5);
  v_sal     number(7, 2);
  v_tax_sal number(7, 2);
begin
  --执行
  select ename, sal into v_ename, v_sal from emp where empno = &no;
  -- 计算所得税
  v_tax_sal := v_sal * c_tax_rate;
  -- 输出
  dbms_output.put_line('' || v_ename || '工资:' || v_sal || ' 交税:' ||
                       v_tax_sal);
end;

选中sql语句块,点击执行之后,弹出弹窗输入员工工号
在这里插入图片描述
执行结果如下:
在这里插入图片描述
在这里插入图片描述

declare
  c_tax_rate number(3, 2) := 0.03;

  -- 用户名
  v_ename   emp.ename%type;
  v_sal     emp.sal%type;
  v_tax_sal number(7, 2);
begin
  --执行
  select ename, sal into v_ename, v_sal from emp where empno = &no;
  -- 计算所得税
  v_tax_sal := v_sal * c_tax_rate;
  -- 输出
  dbms_output.put_line('' || v_ename || '工资:' || v_sal || ' 交税:' ||
                       v_tax_sal);
end;

复合类型

复合变量(composite) - 介绍
用于存放多个值的变量。主要包括这几种

  • pl/sql记录
  • pl/sql表
  • 嵌套表
  • varray

复合类型- pl/sql记录

类似与高级语言中的结构体,需要注意的是,当引用pl/sql记录成员时,必须要加记录变量作为前缀(记录变量.记录成员)如下:

-- pl/sql记录类似java中的类
-- pl/sql记录实例
declare
  -- 定义一个pl/sql记录类型 emp_record_type ,类型包含三个数据ename,salary,title
  type emp_record_type is record(
    ename  emp.ename%type,
    salary emp.sal%type,
    title  emp.job%type);
    sp_record emp_record_type;
  begin  
  -- 定义了一个变量sp_record,这个变量的类型是emp_recrd_type
  select ename,sal,job into sp_record
  from emp where empno=7698;
  dbms_output.put_line('员工姓名:' || sp_record.ename);
end;

执行后控制台输出结果如下:
在这里插入图片描述

复合类型-pl/sql表

相当于高级语言中的数组,但是需要注意的是在高级语言中数组的下标不能为负数,而pl/sql是可以为负数的,并且表元素的下标没有限制,示例如下:

-- pl/sql表实例
declare
-- 定义了一个pl/sql表类型sp_table_type,该类型是用于存放emp.ename%type
-- index by binary_integer 表示下标是整数 
type sp_table_type is table of emp.ename%type index by binary_integer;
-- 定义了一个sp_table变量,这个变量的类型是sp_table_type
sp_table sp_table_type;
/*
说明:
      sp_table_type 是pl/sql表类型
      emp.ename%type 指定了表的元素的类型和长度
      sp_table 为pl/sql表变量
      sp_table(0)则表示下标为0的元素
*/
-- pl/sql中,总是变量名在前,变量类型在后
begin
select ename into sp_table(0) from emp where empno=7902;
dbms_output.put_line('员工姓名:'||sp_table(0));
end;

执行sql语句,输出结果:
在这里插入图片描述

参照变量

参照变量是指用于存放数值指针的变量,通过使用参照变量,可以使得应用程序共享相同对象,从而降低占用的空间。在编写pl/sql程序时,可以使用游标变量(ref cursor)和对象类型变量(ref obj_type)两种参照变量类型

参照变量 - ref cursor游标变量

使用游标时,当定义游标时不需要指定相应的select语句,但是当使用游标时(open时)需要指定select语句,这样一个游标就与一个select语句结合了。实例如下:

-- 使用pl/sql编写一个块,可以输入部门号,并显示该部门所有信息
declare 
-- 定义游标类型 sp_emp_cursor
type sp_emp_cursor is ref cursor;
-- 定义一个游标变量test_cursor是sp_emp_cursor类型
test_cursor sp_emp_cursor;
-- 定义变量
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
--执行
-- 把test_cursor和一个select结合
-- 打开游标变量test_cursor for[指向] select ename,sal from emp where deptno=30
open test_cursor for select ename,sal from emp where deptno=30;
-- 循环取出
loop
    -- fetch 取出
    fetch test_cursor into v_ename,v_sal;
    --判断是否test_cursor为空,为空则退出
    exit when test_cursor%notfound;
    dbms_output.put_line('名字:'||v_ename||'工资:'||v_sal);
end loop;
-- 关闭游标
close test_cursor;
end;

执行sql,输出结果
在这里插入图片描述

异常处理

oracle中将异常分为预定义异常、非预定义异常和 自定义异常三种
预定义异常用于处理常见的oracle错误
非预定义异常用于处理预定义异常外不能处理的异常
自定义异常用于处理与oracle异常无关的其他情况

-- 异常
-- 定义变量
declare v_ename emp.ename%type
begin
-- 把emp表中的ename赋值给v_ename
select ename into v_ename from emp where empno=7780;
-- 控制台打印输出
dbms_output.put_line('名字':||v_ename);
-- 异常
exception
-- oracle预定义异常no_data_found,数据没找到
    when no_data_found then
    dbms_output.put_line('编号不存在');
end;
/

预定义异常

case_not_found

在开发pl/sql编程中,编写case语句时,如果在when子句中没有包含必须的条件分支,就会触发case_not_found

create or replace procedure sp_pro6(spno number) is 
v_sal emp.sal%type;
begin
select sal into v_sal from emp where empno=spno;
case
    when v_sal<1000 then
    update emp set sal=sal+100 where empno=spno;
    when v_sal<2000 then
    update emp set sal=sal+200 where empno=spno;
    end case;
exception
    when case_not_found then
    dbms_output.put_line('case语句没有与v-sal相匹配的条件');
end;
/

cursor_already_open

重新打开已经打开的游标时,会隐含的触发异常cursor_already_open

-- cursor_already_open
declare 
    cursor emp_cursor is select ename,sal from emp;
begin
    open emp_cursor;
    -- 再次打开游标emp_cursor 
    for emp_record in emp_cursor loop
    dbms_output.put_line(emp_record.ename);
    end loop;
exception 
    when cursor_already_open then
    dbms_output.put_line('游标已经打开');
end;
/

dup_val_on_index

在唯一索引所对应的列上插入重复的值,会隐含的触发异常
dup_val_on_index

-- dup_val_on_index
begin
insert into dept values (10,'公关部','北京');
exception
-- 发生dup_val_on_index异常
when dup_val_on_index then
dbms_output.put_line('在deptno列上不能出现重复值');
end;
/

invalid_cursor

当试图在不合法的游标上执行操作时,会触发该异常。
例如:试图从没有打开的游标提取数据,或是关闭没有打开的游标,就会触发invalid_cursor异常

--- invalid_cursor
declare 
cursor emp_cursor is select ename,sal from emp;
emp_record emp_cursor%rowtype;
begin
-- open emp_cursor打开游标
fetch emp_cursor into emp_record;
dbms_output.put_line(emp_record.ename);
exception
    when invalid_cursor then
dbms_output.put_line('请检测游标是否打开');
end;
/

invalid_number

当输入的数据有误时,会触发该异常
比如:数字100写成1oo就会触发该异常

-- invalid_number
begin
update emp set=sal+'1oo';
exception
    when invalid_number then
dbms_output.put_line('输入的数字不正确');
end;
/

too_many_rows

当执行select into 语句时,如果返回超过了一行,则会触发该异常

-- too_many_rows
declare
v_ename emp.ename%type;
begin
    select ename into v_ename from emp;
exception
    when too_many_rows then
dbms_output.put_line('返回了多行');
end;
/

value_error

当在执行赋值操作时,如果变量的长度不足以容纳实际数据,则会触发该异常

-- value_error
declare 
v_ename varchar2(5);
begin
    select ename into v_ename from emp where empno=1;
    dbms_output.put_line(ename);
exception
    when value_error then
     dbms_output.put_line('变量尺寸不足');
end;
/

login_denide

当用户非法登录时,会触发该异常

not_logged_on

如果用户没有登录就执行dml操作,就会触发该异常

storage_error

如果超出了内存空间或是内存被损坏,就触发该异常

timeout_on_resource

如果oracle在等待资源时,出现了超时就触发该异常。

自定义异常

-- 自定义异常
create or replace procedure ex_test(spNo number)
is
-- 定义一个异常
myex exception
begin
-- 更新用户sal
update emp set sal=sal+1000 where empno=1;
-- sql %notfound这是表示没有update
if sql %notfound then
-- raise 扔出异常 myex
raise myex;
end if;
when myex then 
 dbms_output.put_line('没有更新任何用户');
end;
/

视图

  • 表需要占用磁盘空间,视图不需要
  • 视图不能添加所以
  • 使用视图可以简化复杂查询
  • 视图利于提高安全性,比如:不同用户查看不同视图
-- 创建视图
create view 视图名 as select 语句[with read only]
-- 创建或修改视图
create or replace view 视图名 as select 语句[with read only]
-- 删除视图
drop view 视图名;

Oracle--PL/SQL编程 本文主要介绍了 PL/SQL 编程的相关知识。PL/SQL 是 Oracle 数据库中的一种过程化编程语言,以块为基本单位,分为声明、执行和异常处理三部分。文中阐述了其基本语法、数据类型,包括数组、字符、日期、布尔类型等,以及特殊的 %TYPE、RECORD、%ROWTYPE 类型。 同时,详细讲解了流程控制语句,如 IF、CASE、LOOP、WHILE、FOR 和 GOTO 等,并对游标的基本原理、显式游标、隐式游标及其属性进行了说明。此外,还介绍了异常处理的方法,包括预定义异常、用户自定义异常、错误编 阅读详情

相关推荐

Oracle SQL Plus工具与PL/SQL编程实战

SQL Plus是Oracle数据库提供的一个功能强大的交互式命令行工具,它是数据库管理员(DBA)和技术开发人员与Oracle数据库交互的重要手段。通过SQL Plus,用户可以执行SQL语句和PL/SQL块,进行数据查询、修改、备份及恢复等操作。本章将对SQL Plus的定义、历史背景以及其在数据库管理中的重要性做一个简洁的概览,为接下来的深入学习打下基础。-- 示例SQL Plus版本查询SQL Plus作为老牌的数据库管理工具,拥有着悠久的发展历史和广泛的应用基础。

weixin_28235889的博客 1251

DB ORACLE - PLSQL call procedure

How to call procedure in PLSQL of Oracle DB. Note: procedure name, the "()" is not requried ---------------------- [i]Declare Lcntr number; LCalc number; LCalcTmp Number; Begin FOR Lcntr ...

kavinhub 448

Oracle PL/SQL高级编程实战指南

为了定义一个记录类型,我们需要明确记录中包含的字段以及它们的数据类型。定义记录类型的基本语法如下:...下面是一个具体的例子:DECLAREBEGINEND;引用类型可以用来创建对象类型的引用,或者直接引用表中的行。定义引用类型的基本语法如下:使用场景之一是在触发器或程序中直接引用触发事件中的行。例如:DECLAREBEGIN-- 获取特定员工的引用-- 使用引用进行操作END IF;END;

weixin_35732273的博客 1098

存储过程

文章目录一.存储过程1.存储过程得调用2使用call(sql命令)命令调用3使用exec(sqlplus)命令调用1 in2:out3:结合in和out参数二函数函数函数的调用: 一.存储过程 存储过程:是一个有名字得plsql 代码块,一般用来实现某个业务或功能,他没有返回值,但有输出参数(可以将数据传给外部程序),参数类型有三种类型输入参数,输出参数,输入输出参数,存储过程创建之后,会保存到数据库中,当数据库启动时,自动加载到内存中(一次创建多次使用) 存储过程创建语法 create [or repla

ma____n的博客 1503

Autosar开发实战:DaVinci Configurator Pro新建工程保姆级教程(附目录结构解析)

本文详细介绍了使用DaVinci Configurator Pro创建Autosar工程的完整流程,包括环境准备、工程配置、目录结构解析及常见问题排查。通过实战演示和深度解读,帮助开发者快速掌握工具使用技巧,优化工程管理,提升开发效率。特别适合需要理解Autosar开发中DaVinci Config工具链的工程师。

weixin_30872157的博客 454

计算机视觉(相机标定;内参;外参;畸变系数)

目录 一、预备知识 1、坐标系变换过程(相机成像过程) (1)相机坐标系转换为图像坐标系(透视投影变换遵循的是针孔成像原理) (2)齐次坐标的引入原因:(为什么引入齐次坐标???) 2、内参与外参矩阵的构成 3、畸变参数 二、相机标定 1、张正友标定法(光学标定)及其求解思路 2、求解内参矩阵与外参矩阵的思路: (1)计算内参与外参矩阵的乘积M (2)求解内参矩阵A (3)求解外参矩阵(R1 R2 T) 三、参数优化 1、L-M算法 2、相机标定步骤 四、相关代码及其实验结.

QYJ_WORKHARDING的博客 6万+

PLSQL 存储过程

--存储过程 procedure 语法 --相当于没有return的函数 create or replace procedure 存过名(参数1 [in]|out 数据类型) is|as begin [exception] end; --制作一个存过,把emp表的员工信息插入到emp1之后 --更新emp1的员工姓名为小写 --删除1980年入职的员工信息 create or replace procedure pro_1 is begin insert into...

Tapyou的博客 2956

【PDN仿真笔记9-使用Sigrity PowerDC进行IR Drop仿真的方法】

使用Sigrity PowerDC进行IR Drop进行仿真的方法

qq_42682826的博客 7999

pl初学者总结(下)

在运行程序时出现的错误叫做异常发生异常后,语句将停止执行,控制权转移到PL/SQL块的异常处理部分。

wt2337493578的博客 655

advanced installer 打包Flutter的Windows程序安装包(基本打包、开机自启、定时自动检测升级、包含卸载程序)

advanced installer 打包Flutter的Windows程序安装包(基本打包、开机自启、定时自动检测升级、包含卸载程序)

qq_45634989的博客 2878

PythonOCC基础使用:建模——倒角倒圆(二维+三维)

占坑

weixin_42755384的博客 4638

PL/SQL Developer中调试oracle的存储过程

唉,真土,以前用Toad,一直用dbms_output.put_line调试存储过程,只觉得不方便,用上PL/SQL Developer后,习惯性的还是用这个方法,人都是有惰性的。今天分析存储过程生成的数据,实在觉得不便,网上搜了一下,PL/SQL Developer中调试oracle的存储过程的方法,其实很简单。我知道学会使用PL/SQL Developer的调试功能,对于编写复杂的存储过程,包,funtion...非常有帮助,对执行存储过程形成的结果进行分析时也很有用处,学习之后,果然方便,现将相关步骤

驽马十驾 才定不舍 3万+

【学习路线】 游戏客户端开发入门到进阶

游戏开发是一个交叉性极强的技术领域,对开发者提出了高维度的要求。作为初学者,不要急于求成,打牢基础、注重原理、勤于实践,你的成长将会非常快速而稳健。希望这份学习路线与书单能为你指明方向,迈出职业开发者的第一步。

hhw_hhw的博客 2126

Oracle PL/SQL编程详解

Oracle PL/SQL编程详解 - 古立 - 博客园 《我的网络摘抄本》 网摘/转载/备忘/随记 博客园 首页 新随笔 联系 管理 订阅

TDY的博客 2万+

华为OD机试双机位C卷-魔法收积木 (C/C++/Py/Java/Js/Go)

华为OD机试(机考)双机位C卷 【魔法收积木】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为od - 魔法收积木。

qq_45776114的博客 1939

2024年游戏策划最新面试题及答案

1. **游戏策划**:负责游戏设计和开发的创意和技术过程。2. **主要职责**:设计游戏玩法、故事、角色、世界等。4. **游戏机制**:定义玩家如何与游戏世界互动的规则。13. **引人入胜的角色**:深入的背景故事、个性特征。3. **游戏平衡**:确保游戏公平、有趣且具有挑战性。7. **RPG特点**:角色发展、故事驱动、玩家选择。12. **关卡设计技巧**:挑战性、流动性、视觉引导。11. **游戏关卡设计**:创建游戏世界的不同阶段。5. **游戏循环**:玩家在游戏中重复的行为模式。

guo162308的博客 2319

oracle pl/sql 编程,oracle PL/SQL基础编程

PL/SQL(Procedural Language/SQL)是oracle中引入的一种过程化编程语言PLS-00103:出现符号"declare"在需要下列之一时符号"begin"被替换为"declare"后继续存储过程里不需要用declare关键字PLS-00103:出现符号"end-of-file"在需要下列之一时:end要和if以及begin匹配,完了之后要用/来表示完了create or...

weixin_39570777的博客 989

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

oracle PL/SQL编程基础

1 PL/SQL介绍 PL/SQL是在日常工作中,使用oracle数据库常用的一种编程语言,是oracle公司对标准SQL语言的过程化扩展,在SQL语句的基础上增加了编程语言的特点。主要是把数据操作和查询语句放在PL...

1989

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Oracle19C入门到熟练017-PL/SQL编程

SQL也能进行编程, 你相信么?来了解一下Oracle的PL/SQL啊~

langfeiyes的博客 1408

Oracle基础【5-Oracle中的PL/SQL编程】

推荐关注博主!随时获取更多关于Oracle的最新技术分享!

m0_51574586的博客 1400

ORACLE PL/SQL编程

PL/SQL程序设计 什么是PL/SQL PL/SQL是ProcedureLanguage&StructuredQueryLanguage的缩写。PL/SQL是对SQL语言存储过程语言的扩展。从ORACLE6以后,ORACLE的RDBMS附带了PL/SQL。它现在已经成为一种过程处理语言,简称PL/SQL。目前的PL/SQL包括两部分,一部分是数据库引擎部分;另一部分是可...

weixin_30411997的博客 558

Oracle PL/SQL编程规范

摘自:https://www.cnblogs.com/guohu/archive/2014/01/15/3520907.html Oracle PL/SQL编程规范 一、PL/SQL编程规范之大小写 就像在SQL中一样,PL / SQL中是不区分大小写的。其一般准则如下: 关键字(BEGIN, EXCEPTION, END, IF THEN ELSE,LOOP, END L...

kikiwhq的专栏 385

【Oracle】玩转Oracle数据库(五):PL/SQL编程

本文深入介绍了Oracle数据库中的PL/SQL编程,包括存储过程、触发器、函数等重要内容。首先,读者将了解PL/SQL编程的基本结构和语法规则,掌握如何编写简单而有效的存储过程和函数。其次,文章详细讨论了触发器的概念和应用场景,帮助读者理解触发器在数据库操作中的重要性和灵活性。接着,我们深入探讨了PL/SQL中的异常处理机制,以及事务控制的方法,帮助读者构建更健壮和安全的数据库程序。

朝花夕拾 1万+

Oracle PL/SQL 编程入门:第一章 PL/SQL 的基本概述

PL/SQL 引擎:负责解释和执行 PL/SQL 代码。SQL 引擎:负责执行 SQL 语句。Oracle 数据库:存储数据并提供访问接口。PL/SQL 引擎和 SQL 引擎紧密协作,共同完成数据操作任务。当你在 PL/SQL 中执行 SQL 语句时,PL/SQL 引擎会将这些语句传递给 SQL 引擎,后者再与数据库交互并返回结果。在这一章中,我们学习了 PL/SQL 的基本概念、架构、结构以及如何使用不同的开发环境进行编程。PL/SQL 架构。

caifox的博客 1447

Oracle的PL/SQL编程

PL/SQL编程 基本语言风格 PL/SQL把SQL的数据操作功能同过程语言的数据处理功能结合起来。   --声明作用 declare --变量、游标、用户自定义类型、异常 variables,cursors,user_defined,exceptione --程序开始 begin   -SQL   statements   -PL/SQL    statem

午-夜 729

Oracle实验五 PL/SQL编程

实验五 PL/SQL编程 【实验目的】 熟悉PL/SQL的数据类型和书写规则 熟悉控制结构和游标的使用 编写和运行函数、过程和触发器 【实验内容】 编写脚本文件,调试运行脚本文件,并记录结果。 一、在SQL*Plus中编写一个PL/SQL块,功能用于打印学生信息。 在DECLARE部分完成: 建立学生信息记录类型stu_record_type,包括学号,姓名,性别,籍贯,学习成绩和活动成绩。均为可变长字符类型 编写本地子过程:学生信息打印过程PrintStuRecord,把(1)中定义

qq_44458671的博客 2727
上一篇: springboot2中JSON的使用(官网学习)
下一篇: oracle-序列
迷失的小鹿
博客等级 码龄8年 1万+粉丝 199原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值