MySql的存储过程以及JDBC实战 - 详解

news/2025/10/4 18:35:30/文章来源:https://www.cnblogs.com/yxysuanfa/p/19125814

MySql的存储过程以及JDBC实战



前言

本次课程主要是关于Mysql的存储过程实战以及使用JDBC对存储过程进行操作。


一、MySQL存储过程?

MySQL 存储过程是一组预编译的 SQL 语句集合,封装了特定的业务逻辑,可被重复调用。它类似于编程语言中的 “函数”,能减少网络传输、提高执行效率(预编译一次,多次执行),并增强代码复用性。

1、存储过程基本语法

1.1 创建存储过程

-- 修改分隔符(避免与存储过程内的 ; 冲突)
delimiter //
-- 创建存储过程
create procedure 存储过程名(
[参数类型] 参数名 数据类型,  -- 可选:参数(IN/OUT/INOUT)
...
)
begin
-- 存储过程逻辑(SQL语句、控制结构等)
end //
-- 恢复默认分隔符
delimiter ;

重点强调:一定要注意 第一行的 delimiter 与后面的 // 存在一个空格,最后一行的 delimiter 与; 存在空格

参数类型:
IN:输入参数(默认,调用时传入值,过程内可使用)。
OUT:输出参数(过程内赋值,调用后可获取结果)。
INOUT:既输入又输出(调用时传入值,过程内修改后返回)。

1.2 调用存储过程

call 存储过程名([参数值1, 参数值2, ...]);  -- 无参数时直接 call 存储过程名;

1.3 查看存储过程

-- 查看所有存储过程
show procedure status;
-- 查看指定存储过程的创建语句
show create procedure 存储过程名;

1.4 删除存储过程

drop procedure if exists 存储过程名;  -- if exists 避免删除不存在的过程报错

2、存储过程示例

示例 1:无参数的存储过程(查询数据)

创建一个查询 user 表所有数据的存储过程:

delimiter //
create procedure get_all_users()
begin
select * from user;  -- 假设存在 user 表
end //
delimiter ;
-- 调用
call get_all_users();

示例 2:带 IN 参数的存储过程(条件查询)

根据用户 ID 查询用户信息:

delimiter //
create procedure get_user_by_id(in user_id int)  -- 输入参数 user_id
begin
select * from user where id = user_id;
end //
delimiter ;
-- 调用(查询 ID=1 的用户)
call get_user_by_id(1);

示例 3:带 OUT 参数的存储过程(返回结果)

计算 user 表的总记录数,并通过输出参数返回:

delimiter //
create procedure get_user_count(out total int)  -- 输出参数 total
begin
select count(*) into total from user;  -- 将结果存入 total
end //
delimiter ;
-- 调用(需先定义变量接收结果)
set @total_count = 0;  -- 定义用户变量
call get_user_count(@total_count);  -- 传入变量
select @total_count as user_total;  -- 查看结果

示例 4:带 INOUT 参数的存储过程(修改并返回值)

将输入的数字翻倍后返回:

delimiter //
create procedure double_number(inout num int)  -- 既输入又输出
begin
set num = num * 2;  -- 修改参数值
end //
delimiter ;
-- 调用
set @n = 5;  -- 初始值 5
call double_number(@n);  -- 传入并修改
select @n;  -- 结果为 10

示例 5:包含控制结构的存储过程(条件 + 循环)

根据年龄范围批量插入用户(使用 if 条件和 while 循环):

delimiter //
create procedure batch_insert_users(
in start_age int,  -- 起始年龄
in end_age int     -- 结束年龄
)
begin
declare current_age int;  -- 声明局部变量
set current_age = start_age;  -- 初始化
-- 循环插入
while current_age <= end_age do
-- 条件判断:年龄>30 则状态为 'adult',否则为 'young'
if current_age > 30 then
insert into user(name, age, status) values(concat('user_', current_age), current_age, 'adult');
else
insert into user(name, age, status) values(concat('user_', current_age), current_age, 'young');
end if;
set current_age = current_age + 1;  -- 自增
end while;
end //
delimiter ;
-- 调用(插入年龄 20-22 的用户)
call batch_insert_users(20, 22);

3、注意事项

分隔符问题:创建存储过程时必须用 delimiter 修改分隔符(如 //),否则 MySQL 会将过程内的 ; 视为语句结束,导致语法错误。
变量命名:局部变量(declare 声明)仅在过程内有效,避免与表列名或参数名冲突。
权限:创建存储过程需要 CREATE ROUTINE 权限,执行需要 EXECUTE 权限。
调试:MySQL 存储过程调试相对复杂,可通过 select 输出中间结果辅助调试。
适用场景:适合复杂业务逻辑(如多表关联、批量操作),但过度使用可能导致维护困难(建议简单逻辑直接用 SQL 语句)。
通过以上示例,可快速掌握存储过程的创建、调用和常见用法。根据实际业务需求,可组合 SQL 语句和控制结构(if/case/while/repeat 等)实现复杂功能。

二、JDBC调用存储过程的接口:CallableStatement

在 JDBC 中,CallableStatement 是一个接口(继承自 PreparedStatement),专门用于调用数据库中的存储过程。它支持存储过程的输入参数(IN)、输出参数(OUT)和输入输出参数(INOUT),并能处理存储过程的返回结果。

1、核心作用

  • 执行数据库存储过程(如 MySQL、Oracle 等的 PROCEDURE)。
  • 支持存储过程的参数传递(IN/OUT/INOUT)。
  • 相比直接执行 SQL 语句,调用存储过程可减少网络交互,提高效率(尤其适合复杂逻辑)。

2、使用步骤

1.获取数据库连接(Connection)。
2.编写调用存储过程的 SQL 模板:使用 {call 存储过程名(参数列表)} 格式(参数用 ? 占位)。
3.创建 CallableStatement 对象:通过connection.prepareCall(sql) 生成。
4.处理参数:

3、示例代码(结合 MySQL 存储过程)

假设已在 MySQL 中创建以下存储过程,分别演示 IN/OUT/INOUT 参数:

  1. 带 IN 参数的存储过程(查询用户信息)
-- 存储过程:根据用户ID查询用户名
DELIMITER //
CREATE PROCEDURE get_username_by_id(IN user_id INT)
BEGIN
SELECT username FROM users WHERE id = user_id;
END //
DELIMITER ;

JDBC 调用代码:

import java.sql.*;
public class CallableDemo1 {
public static void main(String[] args) {
// 数据库连接信息
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "your_password";
// 调用存储过程的SQL模板
String sql = "{call get_username_by_id(?)}";  // ? 对应 IN 参数 user_id
try (
Connection conn = DriverManager.getConnection(url, user, password);
CallableStatement cstmt = conn.prepareCall(sql)
) {
// 设置 IN 参数(索引从1开始,值为1)
cstmt.setInt(1, 1);
// 执行存储过程
ResultSet rs = cstmt.executeQuery();  // 有返回结果集时用 executeQuery()
// 处理结果
if (rs.next()) {
String username = rs.getString("username");
System.out.println("用户名:" + username);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
  1. 带 OUT 参数的存储过程(返回用户总数)
-- 存储过程:返回用户表总记录数(通过 OUT 参数输出)
DELIMITER //
CREATE PROCEDURE get_user_count(OUT total INT)
BEGIN
SELECT COUNT(*) INTO total FROM users;
END //
DELIMITER ;

JDBC 调用代码:

import java.sql.*;
public class CallableDemo2 {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "your_password";
String sql = "{call get_user_count(?)}";  // ? 对应 OUT 参数 total
try (
Connection conn = DriverManager.getConnection(url, user, password);
CallableStatement cstmt = conn.prepareCall(sql)
) {
// 注册 OUT 参数(索引1,类型为整数)
cstmt.registerOutParameter(1, Types.INTEGER);
// 执行存储过程(无结果集时用 execute())
cstmt.execute();
// 获取 OUT 参数的值
int total = cstmt.getInt(1);
System.out.println("用户总数:" + total);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
  1. 带 INOUT 参数的存储过程(修改并返回值)
-- 存储过程:将输入的数字翻倍后返回(INOUT 参数)
DELIMITER //
CREATE PROCEDURE double_number(INOUT num INT)
BEGIN
SET num = num * 2;
END //
DELIMITER ;

JDBC 调用代码:

import java.sql.*;
public class CallableDemo3 {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "your_password";
String sql = "{call double_number(?)}";  // ? 对应 INOUT 参数 num
try (
Connection conn = DriverManager.getConnection(url, user, password);
CallableStatement cstmt = conn.prepareCall(sql)
) {
// 设置 INOUT 参数的初始值(输入)
cstmt.setInt(1, 5);  // 初始值 5
// 注册 INOUT 参数的输出类型
cstmt.registerOutParameter(1, Types.INTEGER);
// 执行存储过程
cstmt.execute();
// 获取修改后的值(输出)
int result = cstmt.getInt(1);
System.out.println("翻倍后的值:" + result);  // 输出 10
} catch (SQLException e) {
e.printStackTrace();
}
}
}

4、注意事项

  • 参数索引:CallableStatement 的参数索引从 1 开始(而非 0)。
  • 输出参数注册:OUT 和 INOUT 参数必须先通过 registerOutParameter 注册 SQL 类型(如 Types.INTEGER、Types.VARCHAR),否则会抛出异常。
  • 执行方法选择:
    存储过程返回结果集时,用 executeQuery()。
    无结果集但有输出参数时,用 execute()。
  • SQL 类型对应:registerOutParameter 的第二个参数需与数据库字段类型对应(如 MySQL 的 INT 对应 Types.INTEGER,VARCHAR 对应 Types.VARCHAR)。
    资源关闭:始终确保 Connection、CallableStatement、ResultSet 被关闭(try-with-resources 是最佳实践)。
    通过 CallableStatement,JDBC 可以灵活调用各种存储过程,尤其适合需要复用数据库逻辑的场景。

总结

以上就是今天要讲的内容,本文仅仅简单介绍了MySQL的存储过程的使用,以及JDBC的相关接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/927428.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安徽合肥建设银行招聘网站万维网包括哪些网站

springboot745简历系统 获取源码——》公主号&#xff1a;计算机专业毕设大全

做外贸营销型网站浅谈博物馆网站建设意义

目录 基本查找*&#xff1a; 二分查找*&#xff1a; 数据单调递增&#xff1a; 数据单调递减&#xff1a; 总结规律&#xff1a; 插值查找*&#xff1a; 斐波那契查找&#xff08;了解原理&#xff09;&#xff1a;以后补 分块 查找*&#xff1a; 特殊 情况&#xff0…

深圳哪家做网站最好辽宁建设工程信息网ca锁激活

本文目录 本系列文章从新开始为啥要用TypeScript官方文档程序框架从package.json开始tsconfig.jsonJest的配置 jest.config.js服务的实现自动化测试setup.ts文件夹integration 执行及测试对应代码及branch 本系列文章 SAP CAP篇一: 快速创建一个Service&#xff0c;基于Java的…

阿里云万网域名关键词优化排名软件

1. Json 数据解析和绑定 客户端传参&#xff0c;后端接收并解析到结构体 package mainimport ("github.com/gin-gonic/gin""net/http" )// 定义接收数据的结构体 type Login struct {// binding:"required"修饰的字段&#xff0c;若接收为空值…

NFC 贴卡自动拨打微信视频电话

思路来源:b站的视频教程: https://www.bilibili.com/video/BV1Vo3izgEDT/ 文字教程: 下面的操作均在需要贴卡自动拨打微信电话的手机进行:1)下载 NFC Tasks 、NFC Tools、自动脚本精灵,权限打开通知、辅助功能、…

请人建网站wordpress 不做SEO

在项目正式上线之前&#xff0c;我们通常需要通过压测来评估当前系统能够支撑的请求量、排查可能存在的隐藏bug&#xff1b;压力测试&#xff08;压测&#xff09;是确保系统在高负载情况下仍能稳定运行的重要步骤。通过模拟高并发场景&#xff0c;可以评估系统的性能瓶颈、可靠…

实用指南:d-分离:图模型中的条件独立性判定准则

实用指南:d-分离:图模型中的条件独立性判定准则pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

微信嵌入手机网站浙江建设继续教育网站首页

题目背景 思路解析:很经典的贪心问题,把物品按照从便宜到贵的顺序排好序,然后按照富贵程度排人,直接暴力会tle所以这里采用双指针. #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<string> using namesp…

win7可以做网站吗宠物网站模板下载

几年的时间&#xff0c;从OneNote到为知笔记&#xff0c;再到印象笔记&#xff0c;再回到为知笔记和OneNote&#xff0c;用下来感觉各有优势&#xff0c;也有不爽的地方。 早年间OneNote的同步很有问题&#xff0c;一次同步冲突导致失去了几乎所有笔记本。心灰意冷之下就离开了…

[MCP] 监听资源更新

有两种形式:资源列表变化 资源内容变化资源列表变化 用于监听资源目录是否发生增删改。 工作机制 当服务器上的资源列表发生变化时(例如 resources/list 中的项发生增删),服务器主动发送通知: notifications/reso…

详细介绍:Sentinel 深度解析:限流与熔断降级的微服务稳定性保障实践

详细介绍:Sentinel 深度解析:限流与熔断降级的微服务稳定性保障实践2025-10-04 18:15 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto…

GitHub 使用技巧 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

[RAG] 基础知识

RAG基础知识 基本介绍 RAG,全称 Retrieval-Augmented Generation,中文:检索增强生成 核心思想:为大模型补充来自于外部的相关数据与上下文,从而帮助大模型生成更丰富、更准确、更可靠的内容。 也就是 临时给大模型…

CF1408F Two Different

关键是要想到可以“借数”CF1408F Two Different 挺好的一道思维题。 手玩几个例子之后发现以下性质: 1、直接变的话,只能同时把 \(2^k\) 个数变成一样的,次数大概是 \(2^{k - 1} \times k\)。 2、零散的小堆可以向…

青岛住房和城乡建设部网站网站登录不了

在Cocos Creator中&#xff0c;脚本代码文件分为模块和插件两种方式&#xff1a; 模块一般就是项目的脚本&#xff0c;包含项目中创建的代码、引擎模块、第三方模块。 插件脚本&#xff0c;是指从 Cocos Creator 属性检查器中导入的插件&#xff0c;一般是引入第三方引入库文件…

区块链网站用vue.js做怎么样早8晚5双休的工作

TimeStamp 更新操作可能伴随数据冲突&#xff0c;我们可以通过并发处理妥善解决这一方面的问题。避免数据冲突比较方便的做法是自动加入字节数组&#xff08;byte[]&#xff09;类型的TimeStamp属性&#xff0c;对应到数据表中的rowvewsion类型字段&#xff0c;自动监控数据的…

数据结构 - 字典树 Trie

字典树(Trie)是一种树形数据结构,主要用于高效地存储和检索字符串集合。它通过利用字符串的公共前缀来节省存储空间,常用于词典查询、自动补全等场景。 1. 什么是字典树 字典树的每条边代表一个字符,从根节点到某…

激活函数实现

激活函数实现 1.1:创建激活函数工程目录 创建激活函数目录- src (存放源代码)- testbench (存放测试文件)- docs (存放文档)- lut_data (存放查找表数据)- python_utils (存放Python辅助脚本…

漏洞赏金入门指南:从零开始的实战方法论

本文详细介绍了漏洞赏金猎人的入门方法论,包括基础知识学习、时间分配策略、持续学习的重要性,以及如何保持动力和专注度。文章打破了"快速致富"的迷思,为初学者提供了切实可行的成长路径。漏洞赏金入门指…

网站建设数据库搭建电影爱好网

在使用svn的过程中&#xff0c;可能出现整个svn崩溃&#xff0c; 例如cleanup 失败的情况&#xff0c;类似于 这时可以下载本贴资源文件并解压。 或者直接访问网站 SQLite Download Page 进行下载 解压后得到 sqlite3.exe 放到发生问题的svn根目录的.svn路径下 右键呼出pow…