文章目录
- 1.存储过程和存储函数
- 2.创建和使用存储过程
- 2.1 语法:
- 2.2 第一个存储过程,打印hello world
- 2.3 调用语法
- 2.4 带参数的存储过程
- 2.5 调试存储过程
- 3.创建和使用存储函数
- 3.1 存储函数定义
- 3.2 存储函数语法:
- 3.3 存储函数案例:
- 4.in和out参数
- 4.1 介绍
- 4.2 使用原则
- 4.3 案例
- 5.在应用程序中访问存储过程
- 6.在应用程序中访问存储函数
- 7.如何在out函数中使用光标,即out返回集合
- 7.1 使用光标,申明包结构
- 7.2 包头
- 7.3 包体
- 8.代码中使用游标
1.存储过程和存储函数
1.指存储在数据库中供所有用户程序调用的子程序叫存储过程、存储函数。
2.存储过程和存储函数的相同点:完成特定功能的程序。
3.存储过程和存储函数的区别:是否用return语句返回值。
2.创建和使用存储过程
2.1 语法:
语法:
CREATE [OR REPLACE] PROCEDURE 过程名(参数列表)
AS
PLSQL子程序体
解释:
1. CREATE 创建
2. OR REPLACE 替换,存储过程/函数不支持修改,只能创建替换。
3. PROCEDURE 关键字
4. 过程名(参数列表) 存储过程/函数名称,可带参数,也可不带
5. AS 声明部分,比如创建变量
2.2 第一个存储过程,打印hello world
案例:
CREATE OR REPLACE PROCEDURE sayhelloworld
AS
-- 说明部分
BEGIN
DBMS_OUTPUT.PUT_LINE('hello world');
END;
2.3 调用语法
调用存储过程
1.直接调用 exec sayhelloworld()
2.通过其他存储过程调用,如
CREATE OR REPLACE PROCEDURE usesayhelloworld
AS
BEGIN
sayhelloworld();
END;
2.4 带参数的存储过程
例: 给指定员工涨100块钱工资,并打印涨前和涨后的工资。
--创建一个带参数的存储过程
CREATE OR REPLACE PROCEDURE raisemoney(userid in NUMBER)
AS
-- 定义一个变量保存涨前的薪水
raisemoney YSTEXT.MONEY%type; -- mon是变量 YSTEXT.money是表名和字段名
BEGIN
-- 得到员工涨前的薪水,into raisemoney表示赋值
SELECT money into raisemoney FROM YSTEXT WHERE ID = userid;
-- 给该员工涨100工资
UPDATE YSTEXT SET MONEY = MONEY + 100 WHERE ID = userid;
-- 需不需要commit?
-- 注意,一般不在存储过程或者存储函数中,commit和rollback。一般交由调用者处理。
-- 打印
DBMS_OUTPUT.PUT_LINE('涨前:'||raisemoney||' 涨后:'||(raisemoney +100));
END;
1.Navicat直接执行
2.其他的存储过程调用执行
CREATE OR REPLACE PROCEDURE "USERAISEMONEY" AS
BEGIN
RAISEMONEY(1);
RAISEMONEY(1);
commit;
END;
2.5 调试存储过程
在调试中可能会出现问题
此会话需要 DEBUG CONNECT SESSION 和 DEBUG ANY PROCEDURE 用户权限
出现该问题是因为权限不足
操作步骤:
1.打开cmd窗口
2.以管理员身份登录
sqlplus / as sysdba
3.查看本用户权限
USER 为 "SYS"
4.授予调试用户DEBUG CONNECT SESSION 和 DEBUG ANY PROCEDURE权限
grant DEBUG CONNECT SESSION , DEBUG ANY PROCEDURE to YS;
3.创建和使用存储函数
3.1 存储函数定义
1.函数为一命名的存储函数,可带参数,并返回一计算值。
2.函数和过程的结构类似,但是必须返回一个return子句,用于返回函数值。
3.2 存储函数语法:
语法:
CREATE [OR REPLACE] FUNCTION 函数名(参数列表)
return 函数值类型
AS
PLSQL子程序体
3.3 存储函数案例:
案例 : 查询某个员工的年收入
CREATE OR REPLACE FUNCTION queryusermoney(eno IN NUMBER)
return number --定义返回的类型为number
AS
--定义变量保存员工的薪水和奖金
pmoney YSTEXT.MONEY%TYPE;--薪水
pbonus YSTEXT.BONUS%TYPE;--奖金
BEGIN
--得到该员工的薪水和奖金
--into关键字赋值
SELECT MONEY,BONUS INTO pmoney,pbonus FROM YSTEXT WHERE id = eno;
--直接返回年收入
return pmoney*12 + pbonus;
END;
表结构和数据
结果1
查询ID为1的员工,结果为30800,正确
结果2
查询ID为3的员工,结果为空
奖金字段为空,代表为0,所以正确写法
CREATE OR REPLACE FUNCTION queryusermoney(eno IN NUMBER)
return number --定义返回的类型为number
AS
--定义变量保存员工的薪水和奖金
pmoney YSTEXT.MONEY%TYPE;--薪水
pbonus YSTEXT.BONUS%TYPE;--奖金
BEGIN
--得到该员工的薪水和奖金
--into关键字赋值
SELECT MONEY,BONUS INTO pmoney,pbonus FROM YSTEXT WHERE id = eno;
--直接返回年收入
return pmoney*12 + nvl(pbonus,0);
END;
结果3
4.in和out参数
4.1 介绍
1.存储过程和存储函数都可以有out参数。
2.存储过程和存储函数都可以有多个out参数。
3.存储过程可以通过out参数来实现返回值。
4.2 使用原则
那么存储过程和存储函数都能返回一个值,什么时候使用存储函数/存储过程?
原则:
如果只有一个返回值,用存储函数;否则,使用存储过程。
4.3 案例
查询员工的名称,薪水,职位
CREATE OR REPLACE PROCEDURE QUERYUSERINFO(eno IN NUMBER, pname OUT VARCHAR2, pmoney OUT NUMBER, pjob OUT VARCHAR2)
AS
BEGIN
--查询员工的名称,薪水,职位
SELECT NAME,MONEY,JOB INTO pname,pmoney,pjob FROM YSTEXT WHERE id = eno;
END;
表结构和数据
结果
5.在应用程序中访问存储过程
代码实现
1.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>dbtest</artifactId>
<version>1.0-SNAPSHOT</version>
<name>dbtest</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc14</artifactId>
<version>9.0.2.0.0</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>19.3.0.0</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2.JDBCUtils 工具类,创建连接,释放连接。
package storage.utils;
import java.sql.*;
public class JDBCUtils {
private static String driver = "oracle.jdbc.OracleDriver";
private static String url = "jdbc:oracle:thin:@localhost:1521:orcl";
private static String user = "YS";
private static String password = "123456";
//注册数据库驱动
static {
try {
//1.java方式注册驱动
Class.forName(driver);
//2.使用jdbc自带方式注册驱动
//DriverManager.registerDriver(driver);
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}
//获取数据库连接
public static Connection getConnection(){
try{
return DriverManager.getConnection(url,user,password);
}catch (SQLException e){
e.printStackTrace();
}
return null;
}
//释放数据库资源
public static void release(Connection conn, Statement st, ResultSet rs){
if(rs != null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}finally {
rs = null;
}
}
if(st != null){
try{
st.close();
}catch(SQLException e){
e.printStackTrace();
}finally {
st = null;
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
3.RunProcedure 单元测试
package storage.run;
import oracle.jdbc.OracleTypes;
import org.junit.jupiter.api.Test;
import storage.utils.JDBCUtils;
import java.sql.CallableStatement;
import java.sql.Connection;
public class RunProcedure {
@Test
public void runProcedure(){
//调用一个已有的存储过程,如:
// CREATE OR REPLACE PROCEDURE QUERYUSERINFO(eno IN NUMBER, --查询员工的名称,薪水,职位
// pname OUT VARCHAR2,
// pmoney OUT NUMBER,
// pjob OUT VARCHAR2)
// AS
// BEGIN
// --查询员工的名称,薪水,职位
// SELECT NAME,MONEY,JOB INTO pname,pmoney,pjob FROM YSTEXT WHERE id = eno;
// -- DBMS_OUTPUT.PUT_LINE('Navicat for Oracle');
// END;
//存储过程写法,四个问号表示四个参数
String sql = "{call QUERYUSERINFO(?,?,?,?)}";
Connection conn = null;
CallableStatement call = null;
try {
//1.获取数据库连接
conn = JDBCUtils.getConnection();
//2.传入sql,并通过连接获取一个statement
call = conn.prepareCall(sql);
//3.对于输入参数 in,需要赋值
call.setInt(1,1);
//对于输出参数 out,需要申明,申明是一个输出参数.第二个参数为输出参数类型
call.registerOutParameter(2, OracleTypes.VARCHAR);
call.registerOutParameter(3, OracleTypes.NUMBER);
call.registerOutParameter(4, OracleTypes.VARCHAR);
//4.执行调用
call.execute();
//5.取出结果
String name = call.getString(2);
double money = call.getDouble(3);
String job = call.getString(4);
System.out.println(name + "\t" + money + "\t" + job);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.release(conn, call, null);
}
}
}
4.执行结果
数据库数据
运行结果
6.在应用程序中访问存储函数
pom.xml和JDBCUtils工具类和存储过程一致
1.存储函数代码
package storage.run;
/**
* 执行存储函数
*/
import oracle.jdbc.OracleTypes;
import org.junit.jupiter.api.Test;
import storage.utils.JDBCUtils;
import java.sql.CallableStatement;
import java.sql.Connection;
public class RunFunction {
@Test
public void runFunction(){
//调用一个已有的存储函数,如:
// CREATE OR REPLACE FUNCTION QUERYUSERMONEY(eno IN NUMBER) --查询某个员工的年收入
// return number --定义返回的类型为number
// AS
// --定义变量保存员工的薪水和奖金
// pmoney YSTEXT.MONEY%TYPE;--薪水
// pbonus YSTEXT.BONUS%TYPE;--奖金
// BEGIN
// --得到该员工的薪水和奖金
// --into关键字赋值
// SELECT MONEY,BONUS INTO pmoney,pbonus FROM YSTEXT WHERE id = eno;
//
// --直接返回年收入
// return pmoney*12 + nvl(pbonus,0);
// END;
//存储函数写法,最前面问号表示返回值
String sql = "{?=call QUERYUSERMONEY(?)}";
Connection conn = null;
CallableStatement call = null;
try {
//1.获取数据库连接
conn = JDBCUtils.getConnection();
//2.传入sql,并通过连接获取一个statement
call = conn.prepareCall(sql);
//3.对于返回值,需要申明类型
call.registerOutParameter(1,OracleTypes.NUMBER);
//给传入参数赋值
call.setInt(2, 1);
//4.执行调用
call.execute();
//5.取出结果
double yearMoney = call.getDouble(1);
System.out.println("该员工年收入为" + "\t" + yearMoney);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.release(conn, call, null);
}
}
}
2.执行结果
数据库数据
运行结果
7.如何在out函数中使用光标,即out返回集合
7.1 使用光标,申明包结构
包结构包括包头和包体,包同样为数据库的对象,地位和表、视图、序列,存储过程,存储函数一样。
1.包头:只负责申明。
2.包体:只负责实现,包体要实现包头定义的所有存储过程和存储函数。
7.2 包头
语法
--PACKAGE定义包头的关键字
CREATE OR REPLACE PACKAGE MYPACKAGE
AS
-- 定义一个光标参数usrcursor,ref cursor是引用光标的意思
type usrcursor is ref cursor;
-- 调用存储过程
PROCEDURE QUERYUSERLIST(dno IN NUMBER, usrList OUT usrcursor);
END MYPACKAGE;
快速创建包体
7.3 包体
语法
-- PACKAGE BODY定义包体的关键字
CREATE OR REPLACE
PACKAGE BODY MYPACKAGE AS
--存储过程
PROCEDURE QUERYUSERLIST(dno IN NUMBER, usrList OUT usrcursor) AS
BEGIN
--打开光标
open usrList for SELECT * FROM YSTEXT WHERE id = dno;
END QUERYUSERLIST;
END MYPACKAGE;
执行结果
8.代码中使用游标
代码
package storage.run;
import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OracleTypes;
import oracle.sql.NUMBER;
import org.junit.jupiter.api.Test;
import storage.utils.JDBCUtils;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
public class RunCursor {
@Test
public void runCursor(){
//调用一个已有的包结构,如:
// CREATE OR REPLACE
// PACKAGE MYPACKAGE AS --PACKAGE定义包的关键字
// -- 定义一个光标参数usrcursor,ref cursor是引用光标的意思
// type usrcursor is ref cursor;
// -- 调用存储过程
// PROCEDURE QUERYUSERLIST(dno IN NUMBER, usrList OUT usrcursor);
// END MYPACKAGE;
//存储函数写法,最前面问号表示返回值
String sql = "{call MYPACKAGE.QUERYUSERLIST(?,?)}";
Connection conn = null;
CallableStatement call = null;
ResultSet rs = null;
try {
//1.获取数据库连接
conn = JDBCUtils.getConnection();
//2.传入sql,并通过连接获取一个statement
call = conn.prepareCall(sql);
//3.给传入参数赋值
call.setInt(1, 1);
//对于返回值,需要申明类型.这里为游标类型
call.registerOutParameter(2, OracleTypes.CURSOR);
//4.执行调用
call.execute();
//5.取出结果,这里使用CallableStatement接口的实现类
rs = ((OracleCallableStatement)call).getCursor(2);
//6.打印结果
System.out.println("ID" + "\t" + "NAME" + "\t" + "AGE" + "\t" + "MONEY" + "\t" + "DATE" + "\t" + "BONUS" +"\t" + "JOB");
while(rs.next()){
int id = rs.getInt("ID");
String name = rs.getString("NAME");
int age = rs.getInt("AGE");
double money = rs.getDouble("MONEY");
String date = rs.getString("DATE");
double bonus = rs.getDouble("BONUS");
String job = rs.getString("JOB");
System.out.println(id + "\t" + name + "\t" + age + "\t" + money + "\t" + date + "\t" + bonus +"\t" + job);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.release(conn, call, null);
}
}
}
运行结果