Oracle 19c入门学习教程,从入门到精通,Oracle 过程、函数、触发器和包详解(7)

Oracle 过程、函数、触发器和包详解

本章系统讲解 Oracle 数据库中存储过程(Procedure)函数(Function)触发器(Trigger)程序包(Package)的语法、使用方法及最佳实践。这些是 PL/SQL 高级编程的核心组件,广泛用于业务逻辑封装、数据完整性控制和模块化开发。


一、环境准备与安装说明

1. 前提条件

  • 已安装Oracle Database(如 19c、21c 或免费的Oracle XE
  • 已启用SCOTT 用户(用于练习)

2. 启用 SCOTT 用户(若未启用)

-- 以 sysdba 登录CONNECT/ASSYSDBA-- 解锁并设置密码ALTERUSERscott ACCOUNTUNLOCKIDENTIFIEDBYtiger;-- 连接 scottCONNECTscott/tiger

3. 开启输出(用于调试)

SETSERVEROUTPUTON;

✅ 推荐工具:SQL*PlusOracle SQL Developer


二、存储过程(Stored Procedure)

1. 什么是存储过程?

  • 存储在数据库中的命名 PL/SQL 块
  • 可接受参数、执行 DML/DDL、返回结果(通过 OUT 参数)
  • 不返回值(与函数区别)
  • 可被应用程序或其它 PL/SQL 调用

2. 创建存储过程语法

CREATE [OR REPLACE] PROCEDURE procedure_name ( parameter1 [IN | OUT | IN OUT] datatype [DEFAULT value], parameter2 ... ) IS -- 声明变量(可选) BEGIN -- 执行逻辑 [EXCEPTION ...] END [procedure_name]; /

3. 存储过程的参数模式

模式说明
IN默认模式,输入参数(只读)
OUT输出参数(调用后返回值)
IN OUT输入输出参数(可修改)

4. 示例:创建带参数的存储过程

案例1:根据员工编号查询姓名和工资(使用 OUT 参数)
CREATE OR REPLACE PROCEDURE get_emp_info ( p_empno IN emp.empno%TYPE, -- 输入员工编号 p_ename OUT emp.ename%TYPE, -- 输出姓名 p_sal OUT emp.sal%TYPE -- 输出工资 ) IS BEGIN SELECT ename, sal INTO p_ename, p_sal FROM emp WHERE empno = p_empno; EXCEPTION WHEN NO_DATA_FOUND THEN p_ename := NULL; p_sal := NULL; DBMS_OUTPUT.PUT_LINE('Error: Employee ' || p_empno || ' not found.'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Unexpected error: ' || SQLERRM); END get_emp_info; /
调用存储过程(在匿名块中)
DECLARE v_name emp.ename%type; v_sal emp.sal%type; BEGIN get_emp_info(7369, v_name, v_sal); -- 调用过程 DBMS_OUTPUT.PUT_LINE('Name: ' || v_name || ', Salary: ' || v_sal); END; /

5. IN 参数的默认值

-- 创建带默认值的过程 CREATE OR REPLACE PROCEDURE raise_salary ( p_empno IN emp.empno%TYPE, p_percent IN NUMBER DEFAULT 10 -- 默认加薪 10% ) IS BEGIN UPDATE emp SET sal = sal * (1 + p_percent / 100) WHERE empno = p_empno; IF SQL%ROWCOUNT = 0 THEN RAISE_APPLICATION_ERROR(-20001, 'Employee not found'); END IF; COMMIT; DBMS_OUTPUT.PUT_LINE('Salary raised by ' || p_percent || '% for employee ' || p_empno); END raise_salary; /
调用方式(两种)
-- 使用默认值 EXEC raise_salary(7369); -- 显式指定 EXEC raise_salary(7369, 15);

EXEC是 SQL*Plus 快捷命令,等价于BEGIN proc; END;

6. 删除存储过程

DROPPROCEDUREget_emp_info;

三、函数(Function)

1. 什么是函数?

  • 与存储过程类似,但必须返回一个值
  • 可在 SQL 语句中直接调用(如SELECT my_func() FROM dual
  • 不能执行 DML(除非使用自治事务)

2. 创建函数语法

CREATE [OR REPLACE] FUNCTION function_name ( parameter_list ) RETURN return_datatype IS -- 声明 BEGIN -- 逻辑 RETURN expression; END; /

3. 示例:计算员工年薪(含奖金)

CREATE OR REPLACE FUNCTION calc_annual_salary ( p_empno IN emp.empno%TYPE ) RETURN NUMBER IS v_sal emp.sal%TYPE; v_comm emp.comm%TYPE; BEGIN SELECT sal, NVL(comm, 0) INTO v_sal, v_comm FROM emp WHERE empno = p_empno; -- 年薪 = 月薪*12 + 奖金 RETURN (v_sal * 12) + v_comm; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END calc_annual_salary; /

4. 调用函数

方式1:在 SQL 中调用
SELECTename,calc_annual_salary(empno)ASannual_salFROMempWHEREdeptno=20;
方式2:在 PL/SQL 中调用
DECLARE v_annual NUMBER; BEGIN v_annual := calc_annual_salary(7369); DBMS_OUTPUT.PUT_LINE('Annual salary: ' || v_annual); END; /

5. 删除函数

DROPFUNCTIONcalc_annual_salary;

⚠️ 注意:若函数被其他对象依赖,需先删除依赖项。


四、触发器(Trigger)

1. 触发器简介

  • 在特定DML、DDL 或数据库事件发生时自动执行的 PL/SQL 块
  • 用于实现数据完整性、审计、日志、业务规则

2. 触发器类型

类型触发时机说明
语句级每条 DML 语句触发一次无法访问 :OLD/:NEW
行级每行受影响时触发一次可用 :OLD(旧值)、:NEW(新值)
替换触发器(INSTEAD OF)用于视图替代 DML 操作
用户事件触发器LOGON、LOGOFF、DDL 等用于审计

3. 语句级触发器示例

-- 在 emp 表更新后记录操作时间 CREATE OR REPLACE TRIGGER trg_emp_update_stmt AFTER UPDATE ON emp DECLARE BEGIN DBMS_OUTPUT.PUT_LINE('Employee table updated at ' || TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS')); END; /

此触发器对整个 UPDATE 语句触发一次。


4. 行级触发器示例(最常用)

-- 禁止工资降低 CREATE OR REPLACE TRIGGER trg_emp_sal_check BEFORE UPDATE OF sal ON emp FOR EACH ROW -- 关键:行级触发 BEGIN IF :NEW.sal < :OLD.sal THEN RAISE_APPLICATION_ERROR(-20002, 'Error: Salary cannot be decreased!'); END IF; END; /

:OLD.sal是更新前的值,:NEW.sal是更新后的新值。

测试
UPDATEempSETsal=1000WHEREempno=7369;-- 若原工资 >1000,将报错

5. 替换触发器(INSTEAD OF)

用于对不可直接更新的视图执行 DML。

-- 创建视图CREATEVIEWemp_dept_viewASSELECTe.empno,e.ename,d.dnameFROMemp eJOINdept dONe.deptno=d.deptno;-- 创建 INSTEAD OF 触发器CREATEORREPLACETRIGGERtrg_instead_of_insert INSTEADOFINSERTONemp_dept_viewFOR EACH ROWDECLAREv_deptno dept.deptno%TYPE;BEGIN-- 根据部门名查 deptnoSELECTdeptnoINTOv_deptnoFROMdeptWHEREdname=:NEW.dname;-- 插入到 emp 表INSERTINTOemp(empno,ename,deptno)VALUES(:NEW.empno,:NEW.ename,v_deptno);EXCEPTIONWHENNO_DATA_FOUNDTHENRAISE_APPLICATION_ERROR(-20003,'Department not found: '||:NEW.dname);END;/
测试插入
INSERTINTOemp_dept_view(empno,ename,dname)VALUES(8001,'ALICE','SALES');

6. 用户事件触发器(审计登录)

-- 记录用户登录信息(需在 sys 下创建) CREATE TABLE login_audit ( username VARCHAR2(30), logon_time DATE, ip_address VARCHAR2(40) ); CREATE OR REPLACE TRIGGER trg_logon_audit AFTER LOGON ON DATABASE BEGIN INSERT INTO login_audit (username, logon_time, ip_address) VALUES (USER, SYSDATE, SYS_CONTEXT('USERENV', 'IP_ADDRESS')); END; /

此触发器需 DBA 权限,通常用于安全审计。


7. 删除触发器

DROPTRIGGERtrg_emp_sal_check;

五、程序包(Package)

1. 什么是程序包?

  • 规范(Specification):声明公共接口(过程、函数、变量)
  • 主体(Body):实现细节
  • 支持封装、重载、全局变量、初始化块

2. 程序包的优势

  • 模块化设计
  • 提高性能(一次性加载)
  • 支持函数/过程重载
  • 隐藏实现细节

3. 创建程序包规范

-- 包规范:定义公共接口 CREATE OR REPLACE PACKAGE emp_pkg IS -- 公共常量 c_max_salary CONSTANT NUMBER := 10000; -- 过程声明 PROCEDURE hire_employee ( p_empno IN emp.empno%TYPE, p_ename IN emp.ename%TYPE, p_job IN emp.job%TYPE, p_mgr IN emp.mgr%TYPE DEFAULT NULL, p_hiredate IN DATE DEFAULT SYSDATE, p_sal IN emp.sal%TYPE, p_comm IN emp.comm%TYPE DEFAULT NULL, p_deptno IN emp.deptno%TYPE ); -- 函数声明 FUNCTION get_dept_name(p_deptno IN dept.deptno%TYPE) RETURN VARCHAR2; -- 重载函数:根据员工编号或姓名查工资 FUNCTION get_salary(p_empno IN emp.empno%TYPE) RETURN NUMBER; FUNCTION get_salary(p_ename IN emp.ename%TYPE) RETURN NUMBER; END emp_pkg; /

4. 创建程序包主体

CREATE OR REPLACE PACKAGE BODY emp_pkg IS -- 实现 hire_employee PROCEDURE hire_employee ( p_empno IN emp.empno%TYPE, p_ename IN emp.ename%TYPE, p_job IN emp.job%TYPE, p_mgr IN emp.mgr%TYPE DEFAULT NULL, p_hiredate IN DATE DEFAULT SYSDATE, p_sal IN emp.sal%TYPE, p_comm IN emp.comm%TYPE DEFAULT NULL, p_deptno IN emp.deptno%TYPE ) IS BEGIN IF p_sal > c_max_salary THEN RAISE_APPLICATION_ERROR(-20004, 'Salary exceeds maximum allowed.'); END IF; INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES (p_empno, p_ename, p_job, p_mgr, p_hiredate, p_sal, p_comm, p_deptno); COMMIT; DBMS_OUTPUT.PUT_LINE('Employee ' || p_ename || ' hired successfully.'); END hire_employee; -- 实现 get_dept_name FUNCTION get_dept_name(p_deptno IN dept.deptno%TYPE) RETURN VARCHAR2 IS v_dname dept.dname%TYPE; BEGIN SELECT dname INTO v_dname FROM dept WHERE deptno = p_deptno; RETURN v_dname; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN 'Unknown Dept'; END get_dept_name; -- 重载函数1:按编号查工资 FUNCTION get_salary(p_empno IN emp.empno%TYPE) RETURN NUMBER IS v_sal emp.sal%TYPE; BEGIN SELECT sal INTO v_sal FROM emp WHERE empno = p_empno; RETURN v_sal; END get_salary; -- 重载函数2:按姓名查工资 FUNCTION get_salary(p_ename IN emp.ename%TYPE) RETURN NUMBER IS v_sal emp.sal%TYPE; BEGIN SELECT sal INTO v_sal FROM emp WHERE ename = UPPER(p_ename); RETURN v_sal; END get_salary; -- 初始化块(可选) BEGIN DBMS_OUTPUT.PUT_LINE('emp_pkg initialized at ' || SYSDATE); END emp_pkg; /

5. 调用程序包中的成员

-- 调用过程 BEGIN emp_pkg.hire_employee( p_empno => 8002, p_ename => 'BOB', p_job => 'ANALYST', p_sal => 4000, p_deptno=> 20 ); END; / -- 调用函数 SELECT emp_pkg.get_dept_name(20) FROM dual; DECLARE v_sal NUMBER; BEGIN v_sal := emp_pkg.get_salary(7369); -- 按编号 DBMS_OUTPUT.PUT_LINE('Salary by ID: ' || v_sal); v_sal := emp_pkg.get_salary('KING'); -- 按姓名(重载) DBMS_OUTPUT.PUT_LINE('Salary by Name: ' || v_sal); END; /

6. 删除程序包

-- 先删主体,再删规范(或直接删规范)DROPPACKAGE emp_pkg;

删除规范会自动删除主体。


六、综合性实战案例

案例:员工管理系统(含过程、函数、触发器、包)

需求

  1. 创建包hr_mgmt管理员工
  2. 提供入职、加薪、查询功能
  3. 自动记录工资变更日志
  4. 禁止非法操作(如工资为负)
步骤1:创建日志表
CREATETABLEsalary_change_log(log_id NUMBER GENERATED ALWAYSASIDENTITY,empno NUMBER(4),old_sal NUMBER(7,2),new_sal NUMBER(7,2),changed_by VARCHAR2(30)DEFAULTUSER,change_tsDATEDEFAULTSYSDATE);
步骤2:创建触发器(自动记录工资变更)
CREATE OR REPLACE TRIGGER trg_sal_change_log AFTER UPDATE OF sal ON emp FOR EACH ROW BEGIN IF :OLD.sal != :NEW.sal THEN INSERT INTO salary_change_log (empno, old_sal, new_sal) VALUES (:OLD.empno, :OLD.sal, :NEW.sal); END IF; END; /
步骤3:创建程序包规范
CREATE OR REPLACE PACKAGE hr_mgmt IS PROCEDURE add_employee( p_empno NUMBER, p_ename VARCHAR2, p_job VARCHAR2, p_sal NUMBER, p_deptno NUMBER ); PROCEDURE give_raise(p_empno NUMBER, p_amount NUMBER); FUNCTION get_employee_info(p_empno NUMBER) RETURN VARCHAR2; END hr_mgmt; /
步骤4:创建程序包主体
CREATE OR REPLACE PACKAGE BODY hr_mgmt IS PROCEDURE add_employee( p_empno NUMBER, p_ename VARCHAR2, p_job VARCHAR2, p_sal NUMBER, p_deptno NUMBER ) IS BEGIN IF p_sal <= 0 THEN RAISE_APPLICATION_ERROR(-20005, 'Salary must be positive.'); END IF; INSERT INTO emp (empno, ename, job, sal, deptno, hiredate) VALUES (p_empno, UPPER(p_ename), UPPER(p_job), p_sal, p_deptno, SYSDATE); COMMIT; END; PROCEDURE give_raise(p_empno NUMBER, p_amount NUMBER) IS v_current_sal emp.sal%TYPE; BEGIN SELECT sal INTO v_current_sal FROM emp WHERE empno = p_empno; IF v_current_sal + p_amount > 20000 THEN RAISE_APPLICATION_ERROR(-20006, 'New salary exceeds limit.'); END IF; UPDATE emp SET sal = sal + p_amount WHERE empno = p_empno; COMMIT; END; FUNCTION get_employee_info(p_empno NUMBER) RETURN VARCHAR2 IS v_info VARCHAR2(200); BEGIN SELECT ename || ' (' || job || ') - $' || sal INTO v_info FROM emp WHERE empno = p_empno; RETURN v_info; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN 'Employee not found'; END; END hr_mgmt; /
步骤5:测试
-- 添加员工 EXEC hr_mgmt.add_employee(8003, 'Carol', 'Manager', 6000, 10); -- 加薪 EXEC hr_mgmt.give_raise(8003, 500); -- 查询 DECLARE v_info VARCHAR2(200); BEGIN v_info := hr_mgmt.get_employee_info(8003); DBMS_OUTPUT.PUT_LINE(v_info); END; / -- 查看日志 SELECT * FROM salary_change_log;

七、总结

组件特点使用场景
存储过程无返回值,可执行 DML业务操作(如批量处理)
函数有返回值,可在 SQL 中调用计算、转换
触发器自动执行审计、约束、日志
程序包封装多个过程/函数模块化、重用、重载

💡最佳实践

  • 优先使用包组织代码
  • 触发器逻辑尽量简单
  • 函数避免 DML(除非自治事务)
  • 所有 DML 操作考虑异常处理和事务控制

掌握本章内容,即可构建健壮、可维护的 Oracle 数据库应用逻辑。

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

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

相关文章

计算机毕设Java基于Java的小区物业管理系统 基于Java的住宅物业管理智能系统 Java实现的社区物业综合服务平台

计算机毕设Java基于Java的小区物业管理系统p08ye9 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着社区规模的不断扩大&#xff0c;传统的物业管理模式已难以满足居民和物业公…

2026年安徽知名的家教机构怎么选择,封闭式全托集训营/全托补习班/一对一/一对一家教/初中家教,家教机构电话 - 品牌推荐师

随着教育个性化需求的持续增长,上门一对一家教已成为学生提分、家长省心的热门选择。然而,面对市场上琳琅满目的家教机构,如何筛选出资质过硬、师资优质、口碑可靠的服务商?本文基于公开数据、市场调研及行业信息,…

2026 为 npm、yarn 和 pnpm 设置淘宝镜像源

为 npm、yarn 和 pnpm 设置淘宝镜像源&#xff0c;最直接的方法是使用它们各自的命令。以下是淘宝最新镜像地址和对应的设置方法&#xff1a;最新镜像地址&#xff1a; 截至2026年1月&#xff0c;淘宝 npm 镜像的官方最新域名为 https://registry.npmmirror.com。一些旧资料中提…

关于近视的这些问题,不要再搞错了!

在视力健康越来越受关注的当下&#xff0c;关于近视的各种说法层出不穷&#xff0c;不少人因为轻信错误认知&#xff0c;不仅没能有效保护视力&#xff0c;还可能让近视问题愈发严重。近视不是小问题&#xff0c;尤其是对于正在成长发育的青少年来说&#xff0c;避开认知误区&a…

计算机毕设Java基于微信小程序“今天吃什么”随机推荐系统 基于Java实现的微信小程序“智能美食选择助手” Java语言开发的微信小程序“个性化今日菜单推荐系统”

计算机毕设Java基于微信小程序“今天吃什么”随机推荐系统ly6j69&#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。 在快节奏的现代生活中&#xff0c;人们常常面临一个简单却令人纠…

【毕业设计】基于nodejs+微信小程序的垃圾分类和回收系统(源码+文档+远程调试,全bao定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

2025年市面上做得好的SAE法兰采购哪个好,法兰夹/方法兰/内螺纹法兰/SAE法兰/扩口法兰,SAE法兰工厂口碑推荐 - 品牌推荐师

行业洞察:SAE法兰采购市场格局与选择逻辑 随着工业自动化与高端装备制造的快速发展,SAE法兰作为液压系统中的核心连接件,其质量与适配性直接影响设备稳定性与使用寿命。据行业统计,2024年国内SAE法兰市场规模突破8…

中式餐品中式菜品食物检测数据集VOC+YOLO格式6928张238类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数)&#xff1a;6928标注数量(xml文件个数)&#xff1a;6928标注数量(txt文件个数)&#xff1a;6928标注类别…

破局跨境电商“安全盲区”:一氧化碳报警器的风险与可靠感知之道

破局跨境电商“安全盲区”&#xff1a;一氧化碳报警器的风险与可靠感知之道在跨境电商的繁荣市场中&#xff0c;家用安全产品如一氧化碳报警器需求激增&#xff0c;但低价竞争催生了大量不符合安全标准的产品&#xff0c;将消费者置于风险之中&#xff0c;并威胁制造商的品牌声…

计算机小程序毕设实战-基于微信小程序的演出门票管理系统-票务转票系统基于springboot+微信小程序的话剧票务管理系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

Launcher进程启动流程

1、LauncherLauncher作为Android系统的桌面&#xff0c;它的作用有两点&#xff1a; 作为Android系统的启动器&#xff0c;用于启动应用程序&#xff1b; 作为Android系统的桌面&#xff0c;用于显示和管理应用程序的快捷图标或者其它桌面组件&#xff1b;2、Launcher进程启动流…

2026网络安全变局:AI攻防军备竞赛、资产管理革命与人才断层危机

在数字化浪潮席卷全球的第十个年头&#xff0c;网络安全的战场早已不是“黑客与工程师的猫鼠游戏”。2026年&#xff0c;随着生成式AI技术的全面落地与自主智能体&#xff08;Autonomous AI Agent&#xff09;的规模化应用&#xff0c;网络安全将迎来范式级重构——攻防两端的核…

英文文献在哪里找:实用检索渠道及方法指南

做科研的第一道坎&#xff0c;往往不是做实验&#xff0c;也不是写论文&#xff0c;而是——找文献。 很多新手科研小白会陷入一个怪圈&#xff1a;在知网、Google Scholar 上不断换关键词&#xff0c;结果要么信息过载&#xff0c;要么完全抓不到重点。今天分享几个长期使用的…

Spring Boot 实现各种参数校验,写得太好了,建议收藏!

简单使用Java API规范(JSR303)定义了Bean校验的标准validation-api&#xff0c;但没有提供实现。hibernate validation是对这个规范的实现&#xff0c;并增加了校验注解如Email、Length等。Spring Validation是对hibernate validation的二次封装&#xff0c;用于支持spring mvc…

导师严选8个论文写作工具,一键生成论文工具助MBA高效完成毕业论文!

导师严选8个论文写作工具&#xff0c;一键生成论文工具助MBA高效完成毕业论文&#xff01; 论文写作的难题&#xff0c;AI 工具能帮你轻松破解 在 MBA 学习过程中&#xff0c;撰写毕业论文是一项既重要又充满挑战的任务。无论是选题构思、资料收集&#xff0c;还是逻辑梳理与语…

高效论文搜索网站推荐:快速查找学术资源的实用平台

做科研的第一道坎&#xff0c;往往不是做实验&#xff0c;也不是写论文&#xff0c;而是——找文献。 很多新手科研小白会陷入一个怪圈&#xff1a;在知网、Google Scholar 上不断换关键词&#xff0c;结果要么信息过载&#xff0c;要么完全抓不到重点。今天分享几个长期使用的…

SpringBoot对接飞书机器人

账号创建本地是在电脑端&#xff0c;点击左上角的加号&#xff0c;选择创建群组&#xff0c;之后在对应群组页面的右上角打开设置&#xff0c;点击群机器人&#xff0c;添加机器人&#xff0c;选择下面这一个即可点击创建机器人后可以获取到对应的 webhook 地址签名校验&#x…

从“记住我”到“控制我”:Apache Shiro默认密钥反序列化攻击(CVE-2016-4437)深度攻防解析

一、漏洞背景&#xff1a;一个“小功能”引发的全网安全危机 Apache Shiro是Apache基金会旗下的一款轻量级Java安全框架&#xff0c;凭借简单易用、灵活可扩展的特性&#xff0c;被广泛应用于各类Java Web项目的身份认证、授权、会话管理等场景。其中&#xff0c;「记住我&…

Python就业前景好不好?

Python凭借“低门槛广应用”的双重优势&#xff0c;成为当下最具竞争力的编程语言之一。它覆盖数据分析、AI研发、Web开发等多个热门赛道&#xff0c;那么学Python就业前景好吗?以下是详细内容介绍。Python语言具有简单易学、代码可读性高、应用广泛等特点&#xff0c;使其成为…

String、String StringBuffer 和 StringBuilder 的区别是什 么?

String、String StringBuffffer 和 StringBuilder 的区别是什么?String是只读字符串&#xff0c;它并不是基本数据类型&#xff0c;而是一个对象。从底层源码来看是一个final类型的字符数组&#xff0c;所引用的字符串不能被改变&#xff0c;一经定义&#xff0c;无法再增删改…