【Oracle】TCL语言

news2025/6/5 3:17:06

在这里插入图片描述

个人主页:Guiat
归属专栏:Oracle

在这里插入图片描述

文章目录

  • 1. TCL概述
    • 1.1 什么是TCL?
    • 1.2 TCL的核心功能
  • 2. 事务基础概念
    • 2.1 事务的ACID特性
    • 2.2 事务的生命周期
  • 3. COMMIT语句详解
    • 3.1 COMMIT基础语法
    • 3.2 自动提交与手动提交
    • 3.3 提交性能优化
  • 4. ROLLBACK语句详解
    • 4.1 ROLLBACK基础语法
    • 4.2 异常处理中的ROLLBACK
  • 5. SAVEPOINT保存点详解
    • 5.1 SAVEPOINT基础概念
    • 5.2 复杂的SAVEPOINT应用
    • 5.3 SAVEPOINT在批处理中的应用
  • 6. SET TRANSACTION语句
    • 6.1 事务隔离级别
    • 6.2 事务名称和属性设置

正文
TCL(Transaction Control Language)是Oracle数据库的"交通指挥官",专门负责管理数据库事务的流程控制。如果说数据库是一个繁忙的城市,那TCL就是那个指挥交通、确保秩序的红绿灯系统。它决定了什么时候让数据变更"通行"(提交),什么时候要"刹车"(回滚),是保证数据一致性和完整性的关键!

1. TCL概述

1.1 什么是TCL?

TCL就像是数据库的"时间管理大师",它控制着数据变更的节奏和时机。在Oracle这个数据王国里,TCL确保每个数据变更都有始有终,要么完美收官,要么干净利落地撤销,绝不留下半吊子的状态。

TCL事务控制语言
COMMIT提交
ROLLBACK回滚
SAVEPOINT保存点
SET TRANSACTION设置
永久保存变更
释放锁资源
撤销变更
回到起始点
设置检查点
部分回滚
隔离级别
事务属性

1.2 TCL的核心功能

Oracle TCL的功能体系就像一个完整的时间管理系统:

Oracle TCL功能体系
事务控制
锁管理
一致性控制
并发控制
恢复机制
性能优化
开始事务
提交事务
回滚事务
保存点管理
行级锁
表级锁
死锁检测
锁等待
读一致性
写一致性
ACID特性
多版本控制
隔离级别
并发访问
冲突处理
事务排队
自动恢复
手动恢复
实例恢复
介质恢复
批量提交
异步提交
事务分组
资源优化

2. 事务基础概念

2.1 事务的ACID特性

事务就像是一份"保险合同",必须满足四个核心特性:

ACID事务特性
原子性 Atomicity
一致性 Consistency
隔离性 Isolation
持久性 Durability
全部成功或全部失败
不可分割的操作单元
要么做要么不做
数据完整性约束
业务规则一致性
前后状态一致
事务间相互独立
并发控制机制
不同隔离级别
提交后永久保存
系统故障不丢失
持久化存储

2.2 事务的生命周期

-- 事务的典型生命周期演示
BEGIN
    -- 事务自动开始(第一个DML语句)
    DBMS_OUTPUT.PUT_LINE('事务开始时间: ' || TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3'));
    
    -- 第一个DML操作,事务正式开始
    INSERT INTO transaction_log (log_id, operation_type, start_time, status)
    VALUES (txn_log_seq.NEXTVAL, 'DEMO_TRANSACTION', SYSTIMESTAMP, 'STARTED');
    
    -- 设置保存点
    SAVEPOINT operation_1_complete;
    DBMS_OUTPUT.PUT_LINE('保存点设置: operation_1_complete');
    
    -- 执行多个操作
    UPDATE account_balance 
    SET balance = balance - 1000, last_updated = SYSTIMESTAMP
    WHERE account_id = 12345;
    
    UPDATE account_balance 
    SET balance = balance + 1000, last_updated = SYSTIMESTAMP
    WHERE account_id = 67890;
    
    SAVEPOINT transfer_complete;
    DBMS_OUTPUT.PUT_LINE('保存点设置: transfer_complete');
    
    -- 记录操作日志
    INSERT INTO audit_trail (audit_id, transaction_type, amount, from_account, to_account, timestamp)
    VALUES (audit_seq.NEXTVAL, 'TRANSFER', 1000, 12345, 67890, SYSTIMESTAMP);
    
    -- 模拟业务验证
    DECLARE
        v_from_balance NUMBER;
        v_to_balance NUMBER;
    BEGIN
        SELECT balance INTO v_from_balance FROM account_balance WHERE account_id = 12345;
        SELECT balance INTO v_to_balance FROM account_balance WHERE account_id = 67890;
        
        IF v_from_balance < 0 THEN
            DBMS_OUTPUT.PUT_LINE('验证失败: 账户余额不足');
            ROLLBACK TO transfer_complete;
            RAISE_APPLICATION_ERROR(-20001, '账户余额不足');
        ELSE
            DBMS_OUTPUT.PUT_LINE('验证通过: 转账成功');
        END IF;
    END;
    
    -- 提交事务
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('事务提交时间: ' || TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3'));
    
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('事务异常: ' || SQLERRM);
        ROLLBACK;
        DBMS_OUTPUT.PUT_LINE('事务回滚时间: ' || TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3'));
        RAISE;
END;
/

3. COMMIT语句详解

3.1 COMMIT基础语法

COMMIT就像是给所有变更盖上"官方印章",让它们永久生效:

-- 基本COMMIT语法
BEGIN
    INSERT INTO employees (employee_id, first_name, last_name, hire_date)
    VALUES (1001, '张', '三', SYSDATE);
    
    UPDATE employees 
    SET salary = salary * 1.1 
    WHERE department_id = 10;
    
    DELETE FROM employees 
    WHERE status = 'TERMINATED' AND termination_date < ADD_MONTHS(SYSDATE, -24);
    
    -- 提交所有变更
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('所有变更已永久保存');
END;
/

-- 带注释的COMMIT
BEGIN
    UPDATE product_prices 
    SET price = price * 1.05 
    WHERE category = 'ELECTRONICS';
    
    INSERT INTO price_change_log (change_id, change_date, category, change_type, change_percent)
    VALUES (price_log_seq.NEXTVAL, SYSDATE, 'ELECTRONICS', 'INCREASE', 5);
    
    -- 提交并添加注释(在Oracle中,注释主要用于日志记录)
    COMMIT /* 电子产品价格调整 - 涨价5% */;
    DBMS_OUTPUT.PUT_LINE('价格调整已生效');
END;
/

-- 条件COMMIT
DECLARE
    v_processed_count NUMBER := 0;
    v_error_count NUMBER := 0;
    v_batch_size NUMBER := 1000;
BEGIN
    FOR rec IN (SELECT employee_id FROM temp_salary_adjustments) LOOP
        BEGIN
            UPDATE employees 
            SET salary = salary * 1.08, last_updated = SYSDATE
            WHERE employee_id = rec.employee_id;
            
            v_processed_count := v_processed_count + 1;
            
            -- 每处理1000条记录提交一次
            IF MOD(v_processed_count, v_batch_size) = 0 THEN
                COMMIT;
                DBMS_OUTPUT.PUT_LINE('已处理并提交 ' || v_processed_count || ' 条记录');
            END IF;
            
        EXCEPTION
            WHEN OTHERS THEN
                v_error_count := v_error_count + 1;
                DBMS_OUTPUT.PUT_LINE('处理员工 ' || rec.employee_id || ' 时出错: ' || SQLERRM);
        END;
    END LOOP;
    
    -- 提交剩余的记录
    IF MOD(v_processed_count, v_batch_size) != 0 THEN
        COMMIT;
    END IF;
    
    DBMS_OUTPUT.PUT_LINE('处理完成: ' || v_processed_count || ' 成功, ' || v_error_count || ' 失败');
END;
/

3.2 自动提交与手动提交

-- 查看当前自动提交设置
SELECT value FROM v$parameter WHERE name = 'autocommit';

-- 在SQL*Plus中设置自动提交
-- SET AUTOCOMMIT ON;  -- 每个DML语句后自动提交
-- SET AUTOCOMMIT OFF; -- 手动控制提交(推荐)

-- 演示自动提交与手动提交的区别
CREATE OR REPLACE PROCEDURE demo_commit_modes
IS
BEGIN
    DBMS_OUTPUT.PUT_LINE('=== 手动提交模式演示 ===');
    
    -- 开始事务
    INSERT INTO demo_table (id, name, created_date)
    VALUES (1, '测试数据1', SYSDATE);
    
    DBMS_OUTPUT.PUT_LINE('插入数据,但未提交');
    
    -- 在另一个会话中,这条数据是不可见的
    -- 直到执行COMMIT
    
    -- 继续插入更多数据
    INSERT INTO demo_table (id, name, created_date)
    VALUES (2, '测试数据2', SYSDATE);
    
    INSERT INTO demo_table (id, name, created_date)
    VALUES (3, '测试数据3', SYSDATE);
    
    DBMS_OUTPUT.PUT_LINE('插入了3条数据,仍未提交');
    
    -- 统一提交
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('所有数据已提交,现在对其他会话可见');
END;
/

3.3 提交性能优化

-- 批量提交优化
CREATE OR REPLACE PROCEDURE optimized_batch_commit(
    p_batch_size IN NUMBER DEFAULT 10000
)
IS
    CURSOR data_cursor IS
        SELECT * FROM large_source_table WHERE processed_flag = 'N';
    
    TYPE data_array IS TABLE OF large_source_table%ROWTYPE;
    l_data_batch data_array;
    
    v_total_processed NUMBER := 0;
    v_start_time TIMESTAMP := SYSTIMESTAMP;
    v_batch_start_time TIMESTAMP;
    
BEGIN
    DBMS_OUTPUT.PUT_LINE('开始批量处理,批次大小: ' || p_batch_size);
    
    OPEN data_cursor;
    LOOP
        v_batch_start_time := SYSTIMESTAMP;
        
        -- 批量获取数据
        FETCH data_cursor BULK COLLECT INTO l_data_batch LIMIT p_batch_size;
        EXIT WHEN l_data_batch.COUNT = 0;
        
        -- 批量处理
        FORALL i IN 1..l_data_batch.COUNT
            INSERT INTO target_table (
                id, data_field1, data_field2, processed_date
            ) VALUES (
                l_data_batch(i).id,
                l_data_batch(i).data_field1,
                l_data_batch(i).data_field2,
                SYSDATE
            );
        
        -- 更新源表状态
        FORALL i IN 1..l_data_batch.COUNT
            UPDATE large_source_table 
            SET processed_flag = 'Y', processed_date = SYSDATE
            WHERE id = l_data_batch(i).id;
        
        -- 批量提交
        COMMIT;
        
        v_total_processed := v_total_processed + l_data_batch.COUNT;
        
        DBMS_OUTPUT.PUT_LINE(
            '批次完成: ' || l_data_batch.COUNT || ' 条记录, ' ||
            '累计: ' || v_total_processed || ' 条, ' ||
            '批次耗时: ' || EXTRACT(SECOND FROM (SYSTIMESTAMP - v_batch_start_time)) || ' 秒'
        );
        
    END LOOP;
    CLOSE data_cursor;
    
    DBMS_OUTPUT.PUT_LINE(
        '总计处理: ' || v_total_processed || ' 条记录, ' ||
        '总耗时: ' || EXTRACT(SECOND FROM (SYSTIMESTAMP - v_start_time)) || ' 秒'
    );
END;
/

-- 异步提交(Oracle 11g+)
BEGIN
    INSERT INTO audit_log (log_id, operation, timestamp)
    VALUES (audit_seq.NEXTVAL, 'SYSTEM_MAINTENANCE', SYSTIMESTAMP);
    
    -- 异步提交,不等待日志写入完成
    COMMIT WRITE IMMEDIATE NOWAIT;
    
    DBMS_OUTPUT.PUT_LINE('异步提交完成,继续后续操作');
END;
/

-- 同步提交(确保日志写入)
BEGIN
    INSERT INTO critical_audit_log (log_id, operation, timestamp)
    VALUES (critical_audit_seq.NEXTVAL, 'CRITICAL_OPERATION', SYSTIMESTAMP);
    
    -- 同步提交,等待日志写入完成
    COMMIT WRITE IMMEDIATE WAIT;
    
    DBMS_OUTPUT.PUT_LINE('同步提交完成,数据已安全写入');
END;
/

4. ROLLBACK语句详解

4.1 ROLLBACK基础语法

ROLLBACK就像是"时光倒流",让所有变更回到事务开始前的状态:

-- 基本ROLLBACK语法
BEGIN
    INSERT INTO test_table (id, name) VALUES (1, '测试数据');
    UPDATE test_table SET name = '修改后的数据' WHERE id = 1;
    DELETE FROM test_table WHERE id = 999;
    
    DBMS_OUTPUT.PUT_LINE('执行了多个DML操作');
    
    -- 模拟检测到错误
    IF 1 = 1 THEN  -- 某种错误条件
        ROLLBACK;
        DBMS_OUTPUT.PUT_LINE('检测到错误,所有操作已回滚');
    ELSE
        COMMIT;
        DBMS_OUTPUT.PUT_LINE('操作成功,已提交');
    END IF;
END;
/

-- 条件ROLLBACK
DECLARE
    v_account_balance NUMBER;
    v_transfer_amount NUMBER := 5000;
    insufficient_funds EXCEPTION;
BEGIN
    -- 检查账户余额
    SELECT balance INTO v_account_balance 
    FROM accounts 
    WHERE account_id = 12345;
    
    IF v_account_balance < v_transfer_amount THEN
        RAISE insufficient_funds;
    END IF;
    
    -- 执行转账操作
    UPDATE accounts 
    SET balance = balance - v_transfer_amount 
    WHERE account_id = 12345;
    
    UPDATE accounts 
    SET balance = balance + v_transfer_amount 
    WHERE account_id = 67890;
    
    INSERT INTO transaction_history (
        trans_id, from_account, to_account, amount, trans_date
    ) VALUES (
        trans_seq.NEXTVAL, 12345, 67890, v_transfer_amount, SYSDATE
    );
    
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('转账成功完成');
    
EXCEPTION
    WHEN insufficient_funds THEN
        ROLLBACK;
        DBMS_OUTPUT.PUT_LINE('余额不足,转账已取消');
        RAISE_APPLICATION_ERROR(-20001, '账户余额不足,无法完成转账');
    WHEN OTHERS THEN
        ROLLBACK;
        DBMS_OUTPUT.PUT_LINE('转账过程中发生错误: ' || SQLERRM);
        RAISE;
END;
/

-- 在存储过程中的ROLLBACK
CREATE OR REPLACE PROCEDURE process_order_with_rollback(
    p_customer_id IN NUMBER,
    p_product_id IN NUMBER,
    p_quantity IN NUMBER,
    p_order_id OUT NUMBER
)
IS
    v_available_qty NUMBER;
    v_unit_price NUMBER;
    v_total_amount NUMBER;
    
    inventory_insufficient EXCEPTION;
    invalid_customer EXCEPTION;
BEGIN
    -- 验证客户
    DECLARE
        v_customer_count NUMBER;
    BEGIN
        SELECT COUNT(*) INTO v_customer_count
        FROM customers
        WHERE customer_id = p_customer_id AND status = 'ACTIVE';
        
        IF v_customer_count = 0 THEN
            RAISE invalid_customer;
        END IF;
    END;
    
    -- 检查库存
    SELECT quantity_on_hand, unit_price
    INTO v_available_qty, v_unit_price
    FROM products
    WHERE product_id = p_product_id;
    
    IF v_available_qty < p_quantity THEN
        RAISE inventory_insufficient;
    END IF;
    
    -- 计算总金额
    v_total_amount := p_quantity * v_unit_price;
    
    -- 生成订单
    SELECT order_seq.NEXTVAL INTO p_order_id FROM dual;
    
    INSERT INTO orders (order_id, customer_id, order_date, total_amount, status)
    VALUES (p_order_id, p_customer_id, SYSDATE, v_total_amount, 'CONFIRMED');
    
    INSERT INTO order_items (order_id, product_id, quantity, unit_price, line_total)
    VALUES (p_order_id, p_product_id, p_quantity, v_unit_price, v_total_amount);
    
    -- 更新库存
    UPDATE products 
    SET quantity_on_hand = quantity_on_hand - p_quantity
    WHERE product_id = p_product_id;
    
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('订单 ' || p_order_id || ' 创建成功');
    
EXCEPTION
    WHEN invalid_customer THEN
        ROLLBACK;
        RAISE_APPLICATION_ERROR(-20002, '无效的客户ID或客户状态异常');
    WHEN inventory_insufficient THEN
        ROLLBACK;
        RAISE_APPLICATION_ERROR(-20003, '库存不足,当前可用数量: ' || v_available_qty);
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE_APPLICATION_ERROR(-20004, '订单创建失败: ' || SQLERRM);
END;
/

4.2 异常处理中的ROLLBACK

-- 复杂的异常处理和回滚机制
CREATE OR REPLACE PROCEDURE complex_business_transaction
IS
    v_step VARCHAR2(50);
    v_processed_count NUMBER := 0;
    
    -- 自定义异常
    step1_error EXCEPTION;
    step2_error EXCEPTION;
    step3_error EXCEPTION;
    
BEGIN
    DBMS_OUTPUT.PUT_LINE('开始复杂业务事务处理');
    
    -- 第一步:数据验证和预处理
    BEGIN
        v_step := 'STEP1_VALIDATION';
        DBMS_OUTPUT.PUT_LINE('执行步骤: ' || v_step);
        
        -- 模拟数据验证
        FOR rec IN (SELECT * FROM pending_transactions WHERE status = 'PENDING') LOOP
            IF rec.amount <= 0 THEN
                RAISE step1_error;
            END IF;
            v_processed_count := v_processed_count + 1;
        END LOOP;
        
        DBMS_OUTPUT.PUT_LINE('步骤1完成,验证了 ' || v_processed_count || ' 条记录');
        
    EXCEPTION
        WHEN step1_error THEN
            ROLLBACK;
            RAISE_APPLICATION_ERROR(-20101, '步骤1失败:数据验证不通过');
        WHEN OTHERS THEN
            ROLLBACK;
            RAISE_APPLICATION_ERROR(-20102, '步骤1异常:' || SQLERRM);
    END;
    
    -- 第二步:主要业务逻辑处理
    BEGIN
        v_step := 'STEP2_PROCESSING';
        DBMS_OUTPUT.PUT_LINE('执行步骤: ' || v_step);
        
        -- 批量处理业务逻辑
        UPDATE pending_transactions 
        SET status = 'PROCESSING', 
            processing_date = SYSDATE
        WHERE status = 'PENDING';
        
        -- 插入处理记录
        INSERT INTO transaction_log (log_id, step_name, processed_count, log_date)
        VALUES (log_seq.NEXTVAL, v_step, v_processed_count, SYSDATE);
        
        DBMS_OUTPUT.PUT_LINE('步骤2完成,处理了 ' || SQL%ROWCOUNT || ' 条记录');
        
    EXCEPTION
        WHEN step2_error THEN
            ROLLBACK;
            RAISE_APPLICATION_ERROR(-20201, '步骤2失败:业务处理错误');
        WHEN OTHERS THEN
            ROLLBACK;
            RAISE_APPLICATION_ERROR(-20202, '步骤2异常:' || SQLERRM);
    END;
    
    -- 第三步:结果确认和清理
    BEGIN
        v_step := 'STEP3_FINALIZATION';
        DBMS_OUTPUT.PUT_LINE('执行步骤: ' || v_step);
        
        -- 最终确认
        UPDATE pending_transactions 
        SET status = 'COMPLETED', 
            completion_date = SYSDATE
        WHERE status = 'PROCESSING';
        
        -- 清理临时数据
        DELETE FROM temp_processing_data 
        WHERE created_date < SYSDATE - 1;
        
        DBMS_OUTPUT.PUT_LINE('步骤3完成,清理了 ' || SQL%ROWCOUNT || ' 条临时数据');
        
    EXCEPTION
        WHEN step3_error THEN
            ROLLBACK;
            RAISE_APPLICATION_ERROR(-20301, '步骤3失败:最终确认错误');
        WHEN OTHERS THEN
            ROLLBACK;
            RAISE_APPLICATION_ERROR(-20302, '步骤3异常:' || SQLERRM);
    END;
    
    -- 所有步骤成功,提交事务
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('复杂业务事务处理成功完成');
    
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        DBMS_OUTPUT.PUT_LINE('事务在步骤 ' || v_step || ' 失败,已全部回滚');
        
        -- 记录错误日志
        INSERT INTO error_log (error_id, error_step, error_message, error_date)
        VALUES (error_seq.NEXTVAL, v_step, SQLERRM, SYSDATE);
        COMMIT;  -- 单独提交错误日志
        
        RAISE;
END;
/

5. SAVEPOINT保存点详解

5.1 SAVEPOINT基础概念

SAVEPOINT就像是游戏中的"存档点",可以在事务中设置多个检查点:

SAVEPOINT工作机制
设置保存点
回滚到保存点
释放保存点
嵌套保存点
SAVEPOINT name
标记当前位置
可设置多个
ROLLBACK TO name
部分回滚
保留后续操作
COMMIT自动释放
ROLLBACK自动释放
手动管理
保存点层次
覆盖同名保存点
灵活回滚控制
-- 基本SAVEPOINT使用
DECLARE
    v_operation_step NUMBER := 0;
BEGIN
    DBMS_OUTPUT.PUT_LINE('=== SAVEPOINT演示开始 ===');
    
    -- 第一步操作
    v_operation_step := 1;
    INSERT INTO demo_table (id, step, description, created_date)
    VALUES (1, v_operation_step, '第一步操作', SYSDATE);
    
    SAVEPOINT step1_complete;
    DBMS_OUTPUT.PUT_LINE('步骤1完成,设置保存点: step1_complete');
    
    -- 第二步操作
    v_operation_step := 2;
    INSERT INTO demo_table (id, step, description, created_date)
    VALUES (2, v_operation_step, '第二步操作', SYSDATE);
    
    UPDATE demo_table SET description = description || ' - 已更新' 
    WHERE step = 1;
    
    SAVEPOINT step2_complete;
    DBMS_OUTPUT.PUT_LINE('步骤2完成,设置保存点: step2_complete');
    
    -- 第三步操作(模拟出错)
    v_operation_step := 3;
    BEGIN
        INSERT INTO demo_table (id, step, description, created_date)
        VALUES (3, v_operation_step, '第三步操作', SYSDATE);
        
        -- 模拟业务逻辑错误
        IF SYSDATE > DATE '1900-01-01' THEN
            RAISE_APPLICATION_ERROR(-20001, '模拟的业务错误');
        END IF;
        
        SAVEPOINT step3_complete;
        DBMS_OUTPUT.PUT_LINE('步骤3完成,设置保存点: step3_complete');
        
    EXCEPTION
        WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE('步骤3发生错误: ' || SQLERRM);
            ROLLBACK TO step2_complete;
            DBMS_OUTPUT.PUT_LINE('已回滚到保存点: step2_complete');
    END;
    
    -- 第四步操作(替代第三步)
    v_operation_step := 4;
    INSERT INTO demo_table (id, step, description, created_date)
    VALUES (4, v_operation_step, '第四步操作(替代第三步)', SYSDATE);
    
    SAVEPOINT step4_complete;
    DBMS_OUTPUT.PUT_LINE('步骤4完成,设置保存点: step4_complete');
    
    -- 提交所有成功的操作
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('=== 事务提交,所有保存点自动释放 ===');
    
END;
/

-- 查看最终结果
SELECT id, step, description, created_date 
FROM demo_table 
ORDER BY id;

5.2 复杂的SAVEPOINT应用

-- 多层次保存点管理
CREATE OR REPLACE PROCEDURE multi_level_savepoint_demo
IS
    v_level1_success BOOLEAN := FALSE;
    v_level2_success BOOLEAN := FALSE;
    v_level3_success BOOLEAN := FALSE;
BEGIN
    DBMS_OUTPUT.PUT_LINE('开始多层次保存点演示');
    
    -- 第一层操作
    BEGIN
        DBMS_OUTPUT.PUT_LINE('执行第一层操作...');
        
        INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)
        VALUES (log_seq.NEXTVAL, 1, 'LEVEL1_OPERATION', 'STARTED', SYSTIMESTAMP);
        
        -- 模拟一些复杂操作
        FOR i IN 1..5 LOOP
            INSERT INTO temp_data (id, data_value, level_num)
            VALUES (i, 'Level1_Data_' || i, 1);
        END LOOP;
        
        SAVEPOINT level1_complete;
        v_level1_success := TRUE;
        DBMS_OUTPUT.PUT_LINE('第一层操作完成,设置保存点: level1_complete');
        
        -- 第二层操作
        BEGIN
            DBMS_OUTPUT.PUT_LINE('执行第二层操作...');
            
            INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)
            VALUES (log_seq.NEXTVAL, 2, 'LEVEL2_OPERATION', 'STARTED', SYSTIMESTAMP);
            
            -- 更新第一层的数据
            UPDATE temp_data 
            SET data_value = data_value || '_UPDATED_BY_LEVEL2'
            WHERE level_num = 1;
            
            -- 添加第二层数据
            FOR i IN 6..10 LOOP
                INSERT INTO temp_data (id, data_value, level_num)
                VALUES (i, 'Level2_Data_' || i, 2);
            END LOOP;
            
            SAVEPOINT level2_complete;
            v_level2_success := TRUE;
            DBMS_OUTPUT.PUT_LINE('第二层操作完成,设置保存点: level2_complete');
            
            -- 第三层操作
            BEGIN
                DBMS_OUTPUT.PUT_LINE('执行第三层操作...');
                
                INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)
                VALUES (log_seq.NEXTVAL, 3, 'LEVEL3_OPERATION', 'STARTED', SYSTIMESTAMP);
                
                -- 模拟第三层可能失败的操作
                FOR i IN 11..15 LOOP
                    INSERT INTO temp_data (id, data_value, level_num)
                    VALUES (i, 'Level3_Data_' || i, 3);
                    
                    -- 模拟在第13个操作时失败
                    IF i = 13 THEN
                        RAISE_APPLICATION_ERROR(-20003, '第三层操作模拟失败');
                    END IF;
                END LOOP;
                
                SAVEPOINT level3_complete;
                v_level3_success := TRUE;
                DBMS_OUTPUT.PUT_LINE('第三层操作完成,设置保存点: level3_complete');
                
            EXCEPTION
                WHEN OTHERS THEN
                    DBMS_OUTPUT.PUT_LINE('第三层操作失败: ' || SQLERRM);
                    ROLLBACK TO level2_complete;
                    DBMS_OUTPUT.PUT_LINE('已回滚到保存点: level2_complete');
                    
                    -- 记录第三层失败
                    INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)
                    VALUES (log_seq.NEXTVAL, 3, 'LEVEL3_OPERATION', 'FAILED', SYSTIMESTAMP);
            END;
            
        EXCEPTION
            WHEN OTHERS THEN
                DBMS_OUTPUT.PUT_LINE('第二层操作失败: ' || SQLERRM);
                ROLLBACK TO level1_complete;
                DBMS_OUTPUT.PUT_LINE('已回滚到保存点: level1_complete');
                
                INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)
                VALUES (log_seq.NEXTVAL, 2, 'LEVEL2_OPERATION', 'FAILED', SYSTIMESTAMP);
        END;
        
    EXCEPTION
        WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE('第一层操作失败: ' || SQLERRM);
            ROLLBACK;
            DBMS_OUTPUT.PUT_LINE('已回滚整个事务');
            
            INSERT INTO operation_log (log_id, level_num, operation, status, timestamp)
            VALUES (log_seq.NEXTVAL, 1, 'LEVEL1_OPERATION', 'FAILED', SYSTIMESTAMP);
            COMMIT;  -- 只提交错误日志
            RETURN;
    END;
    
    -- 更新操作状态
    IF v_level1_success THEN
        UPDATE operation_log 
        SET status = 'COMPLETED' 
        WHERE level_num = 1 AND operation = 'LEVEL1_OPERATION' AND status = 'STARTED';
    END IF;
    
    IF v_level2_success THEN
        UPDATE operation_log 
        SET status = 'COMPLETED' 
        WHERE level_num = 2 AND operation = 'LEVEL2_OPERATION' AND status = 'STARTED';
    END IF;
    
    IF v_level3_success THEN
        UPDATE operation_log 
        SET status = 'COMPLETED' 
        WHERE level_num = 3 AND operation = 'LEVEL3_OPERATION' AND status = 'STARTED';
    END IF;
    
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('多层次操作完成,最终状态已提交');
    
    -- 输出最终统计
    DBMS_OUTPUT.PUT_LINE('操作结果统计:');
    DBMS_OUTPUT.PUT_LINE('第一层: ' || CASE WHEN v_level1_success THEN '成功' ELSE '失败' END);
    DBMS_OUTPUT.PUT_LINE('第二层: ' || CASE WHEN v_level2_success THEN '成功' ELSE '失败' END);
    DBMS_OUTPUT.PUT_LINE('第三层: ' || CASE WHEN v_level3_success THEN '成功' ELSE '失败' END);
END;
/

5.3 SAVEPOINT在批处理中的应用

-- 大批量数据处理中的保存点应用
CREATE OR REPLACE PROCEDURE batch_process_with_savepoints(
    p_batch_size IN NUMBER DEFAULT 1000,
    p_max_errors IN NUMBER DEFAULT 10
)
IS
    CURSOR data_cursor IS
        SELECT rowid as row_id, id, data_field1, data_field2
        FROM large_source_table
        WHERE processed_flag = 'N'
        ORDER BY id;
    
    TYPE rowid_array IS TABLE OF ROWID;
    TYPE number_array IS TABLE OF NUMBER;
    TYPE varchar_array IS TABLE OF VARCHAR2(100);
    
    l_row_ids rowid_array;
    l_ids number_array;
    l_data1 varchar_array;
    l_data2 varchar_array;
    
    v_batch_count NUMBER := 0;
    v_total_processed NUMBER := 0;
    v_total_errors NUMBER := 0;
    v_current_batch_errors NUMBER := 0;
    
BEGIN
    DBMS_OUTPUT.PUT_LINE('开始批量处理,批次大小: ' || p_batch_size);
    DBMS_OUTPUT.PUT_LINE('最大错误容忍数: ' || p_max_errors);
    
    OPEN data_cursor;
    LOOP
        -- 批量获取数据
        FETCH data_cursor BULK COLLECT 
        INTO l_row_ids, l_ids, l_data1, l_data2
        LIMIT p_batch_size;
        
        EXIT WHEN l_row_ids.COUNT = 0;
        
        v_batch_count := v_batch_count + 1;
        v_current_batch_errors := 0;
        
        DBMS_OUTPUT.PUT_LINE('处理第 ' || v_batch_count || ' 批,记录数: ' || l_row_ids.COUNT);
        
        -- 设置批次开始保存点
        SAVEPOINT batch_start;
        
        -- 逐条处理当前批次
        FOR i IN 1..l_row_ids.COUNT LOOP
            BEGIN
                -- 设置单条记录保存点
                SAVEPOINT record_start;
                
                -- 复杂的业务处理逻辑
                INSERT INTO target_table1 (id, processed_data, created_date)
                VALUES (l_ids(i), UPPER(l_data1(i)), SYSDATE);
                
                INSERT INTO target_table2 (source_id, calculated_value, created_date)
                VALUES (l_ids(i), LENGTH(l_data2(i)) * 10, SYSDATE);
                
                -- 更新源表状态
                UPDATE large_source_table
                SET processed_flag = 'Y', processed_date = SYSDATE
                WHERE rowid = l_row_ids(i);
                
                v_total_processed := v_total_processed + 1;
                
            EXCEPTION
                WHEN OTHERS THEN
                    -- 单条记录处理失败,回滚到记录开始
                    ROLLBACK TO record_start;
                    
                    v_current_batch_errors := v_current_batch_errors + 1;
                    v_total_errors := v_total_errors + 1;
                    
                    DBMS_OUTPUT.PUT_LINE(
                        '记录 ' || l_ids(i) || ' 处理失败: ' || SQLERRM
                    );
                    
                    -- 记录错误信息
                    INSERT INTO error_log (
                        error_id, source_id, error_message, error_date, batch_number
                    ) VALUES (
                        error_seq.NEXTVAL, l_ids(i), SQLERRM, SYSDATE, v_batch_count
                    );
                    
                    -- 检查是否超过错误容忍限制
                    IF v_total_errors > p_max_errors THEN
                        DBMS_OUTPUT.PUT_LINE('错误数超过限制,终止处理');
                        ROLLBACK TO batch_start;
                        CLOSE data_cursor;
                        RAISE_APPLICATION_ERROR(-20100, '错误数超过限制: ' || p_max_errors);
                    END IF;
            END;
        END LOOP;
        
        -- 检查当前批次错误率
        IF v_current_batch_errors > l_row_ids.COUNT * 0.1 THEN
            DBMS_OUTPUT.PUT_LINE('当前批次错误率过高,回滚整个批次');
            ROLLBACK TO batch_start;
        ELSE
            -- 提交当前批次
            COMMIT;
            DBMS_OUTPUT.PUT_LINE(
                '第 ' || v_batch_count || ' 批处理完成,' ||
                '成功: ' || (l_row_ids.COUNT - v_current_batch_errors) || ',' ||
                '失败: ' || v_current_batch_errors
            );
        END IF;
        
    END LOOP;
    CLOSE data_cursor;
    
    DBMS_OUTPUT.PUT_LINE('批量处理完成');
    DBMS_OUTPUT.PUT_LINE('总计处理: ' || v_total_processed || ' 条记录');
    DBMS_OUTPUT.PUT_LINE('总计错误: ' || v_total_errors || ' 条记录');
    DBMS_OUTPUT.PUT_LINE('处理批次: ' || v_batch_count || ' 个批次');
    
END;
/

6. SET TRANSACTION语句

6.1 事务隔离级别

SET TRANSACTION就像是给事务设定"行为准则":

Oracle隔离级别
READ COMMITTED
SERIALIZABLE
READ ONLY
默认级别
语句级读一致性
避免脏读
允许不可重复读
事务级读一致性
避免幻象读
最高隔离级别
可能降低并发性
只读事务
不允许DML
查询一致性快照
适合报表查询
-- READ COMMITTED隔离级别(默认)
BEGIN
    -- 显式设置READ COMMITTED(通常不需要,因为是默认值)
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    
    DBMS_OUTPUT.PUT_LINE('使用READ COMMITTED隔离级别');
    
    -- 在这个级别下,每个查询都能看到查询开始时已提交的数据
    SELECT COUNT(*) as current_count FROM employees;
    
    -- 如果其他会话在此时插入并提交了新记录,下一个查询会看到它们
    DBMS_LOCK.SLEEP(5);  -- 等待5秒,模拟其他会话操作
    
    SELECT COUNT(*) as updated_count FROM employees;
    
    COMMIT;
END;
/

-- SERIALIZABLE隔离级别
BEGIN
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    
    DBMS_OUTPUT.PUT_LINE('使用SERIALIZABLE隔离级别');
    
    -- 记录事务开始时的SCN
    DECLARE
        v_start_scn NUMBER;
        v_emp_count1 NUMBER;
        v_emp_count2 NUMBER;
    BEGIN
        SELECT CURRENT_SCN INTO v_start_scn FROM v$database;
        DBMS_OUTPUT.PUT_LINE('事务开始SCN: ' || v_start_scn);
        
        SELECT COUNT(*) INTO v_emp_count1 FROM employees;
        DBMS_OUTPUT.PUT_LINE('第一次查询员工数: ' || v_emp_count1);
        
        -- 即使其他会话提交了新数据,在SERIALIZABLE模式下
        -- 本事务仍然只能看到事务开始时的数据快照
        DBMS_LOCK.SLEEP(10);
        
        SELECT COUNT(*) INTO v_emp_count2 FROM employees;
        DBMS_OUTPUT.PUT_LINE('第二次查询员工数: ' || v_emp_count2);
        
        IF v_emp_count1 = v_emp_count2 THEN
            DBMS_OUTPUT.PUT_LINE('SERIALIZABLE确保了读一致性');
        END IF;
    END;
    
    COMMIT;
END;
/

-- READ ONLY事务
BEGIN
    SET TRANSACTION READ ONLY;
    
    DBMS_OUTPUT.PUT_LINE('只读事务开始');
    
    -- 生成一致性报表
    DECLARE
        v_report_time TIMESTAMP := SYSTIMESTAMP;
    BEGIN
        DBMS_OUTPUT.PUT_LINE('报表生成时间: ' || TO_CHAR(v_report_time, 'YYYY-MM-DD HH24:MI:SS'));
        
        -- 部门统计
        FOR dept_rec IN (
            SELECT d.department_name, COUNT(e.employee_id) as emp_count, AVG(e.salary) as avg_salary
            FROM departments d
            LEFT JOIN employees e ON d.department_id = e.department_id
            GROUP BY d.department_name
            ORDER BY d.department_name
        ) LOOP
            DBMS_OUTPUT.PUT_LINE(
                '部门: ' || dept_rec.department_name || 
                ', 员工数: ' || dept_rec.emp_count || 
                ', 平均薪资: ' || ROUND(dept_rec.avg_salary, 2)
            );
        END LOOP;
        
        -- 在只读事务中,所有查询都看到同一个时间点的数据快照
        -- 确保报表数据的一致性
        
        -- 尝试执行DML会失败
        BEGIN
            INSERT INTO test_table VALUES (1, '测试');
        EXCEPTION
            WHEN OTHERS THEN
                DBMS_OUTPUT.PUT_LINE('只读事务中不能执行DML: ' || SQLERRM);
        END;
    END;
    
    COMMIT;  -- 或者 ROLLBACK,对只读事务效果相同
END;
/

6.2 事务名称和属性设置

-- 设置事务名称(便于监控和调试)
BEGIN
    SET TRANSACTION NAME '月度财务结算';
    
    DBMS_OUTPUT.PUT_LINE('开始执行月度财务结算事务');
    
    -- 复杂的财务处理逻辑
    INSERT INTO monthly_summary (month_year, total_revenue, total_expenses, net_profit)
    SELECT 
        TO_CHAR(SYSDATE, 'YYYY-MM'),
        SUM(CASE WHEN transaction_type = 'REVENUE' THEN amount ELSE 0 END),
        SUM(CASE WHEN transaction_type = 'EXPENSE' THEN amount ELSE 0 END),
        SUM(CASE WHEN transaction_type = 'REVENUE' THEN amount ELSE -amount END)
    FROM financial_transactions
    WHERE transaction_date >= TRUNC(SYSDATE, 'MM')
      AND transaction_date < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1);
    
    -- 更新账户余额
    UPDATE account_balances ab
    SET current_balance = (
        SELECT ab.current_balance + COALESCE(SUM(
            CASE WHEN ft.transaction_type = 'CREDIT' THEN ft.amount ELSE -ft.amount END
        ), 0)
        FROM financial_transactions ft
        WHERE ft.account_id = ab.account_id
          AND ft.transaction_date >= TRUNC(SYSDATE, 'MM')
          AND ft.transaction_date < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1)
          AND ft.processed_flag = 'N'
    );
    
    -- 标记已处理的交易
    UPDATE financial_transactions 
    SET processed_flag = 'Y', processed_date = SYSDATE
    WHERE transaction_date >= TRUNC(SYSDATE, 'MM')
      AND transaction_date < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1)
      AND processed_flag = 'N';
    
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('月度财务结算完成');
END;
/

-- 设置事务使用特定回滚段
BEGIN
    -- 在旧版本Oracle中可以指定回滚段(现在通常由Oracle自动管理)
    -- SET TRANSACTION USE ROLLBACK SEGMENT rbs_large;
    
    DBMS_OUTPUT.PUT_LINE('开始大型数据操作事务');
    
    -- 大批量数据操作
    INSERT INTO archive_table
    SELECT * FROM active_table
    WHERE created_date < ADD_MONTHS(SYSDATE, -24);
    
    DELETE FROM active_table
    WHERE created_date < ADD_MONTHS(SYSDATE, -24);
    
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('数据归档操作完成');
END;
/

-- 组合使用多个事务属性
BEGIN
    SET TRANSACTION 
        ISOLATION LEVEL SERIALIZABLE
        NAME '关键业务一致性检查';
    
    DBMS_OUTPUT.PUT_LINE('开始关键业务一致性检查');
    
    -- 在SERIALIZABLE级别下执行一致性检查
    DECLARE
        v_accounts_total NUMBER;
        v_transactions_total NUMBER;
        v_difference NUMBER;
    BEGIN
        -- 计算所有账户余额总和
        SELECT SUM(balance) INTO v_accounts_total FROM account_balances;
        
        -- 计算所有交易净额总和
        SELECT SUM(CASE WHEN transaction_type = 'CREDIT' THEN amount ELSE -amount END)
        INTO v_transactions_total FROM all_transactions;
        
        v_difference := ABS(v_accounts_total - v_transactions_total);
        
        DBMS_OUTPUT.PUT_LINE('账户余额总和: ' || v_accounts_total);
        DBMS_OUTPUT.PUT_LINE('交易净额总和: ' || v_transactions_total);
        DBMS_OUTPUT.PUT_LINE('差额: ' || v_difference);
        
        IF v_difference > 0.01 THEN  -- 允许1分钱的舍入误差
            RAISE_APPLICATION_ERROR(-20200, '数据不一致,差额: ' || v_difference);
        ELSE
            DBMS_OUTPUT.PUT_LINE('一致性检查通过');
        END IF;
    END;
    
    COMMIT;
END;
/

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

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

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

相关文章

Windows | 总误按Num Lock?修改注册表永久禁用Numlk键使小键盘一直输入数字

先说需修改注册表的位置与键值 路径&#xff1a;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout\ 二进制键&#xff1a;Scancode Map 键值&#xff1a; 00 00 00 00 00 00 00 00 01 00 00 00 00 00 45 00 00 00 00 00 00 00 00 00如下图&#xff1a; …

2.RV1126-OPENCV Mat理解和AT函数

一.Mat概念 Mat 是整个图像存储的核心也是所有图像处理的最基础的类&#xff0c;Mat 主要存储图像的矩阵类型&#xff0c;包括向量、矩阵、灰度或者彩色图像等等。Mat由两部分组成&#xff1a;矩阵头&#xff0c;矩阵数据。矩阵头是存储图像的长度、宽度、色彩信息等头部信息&a…

unidbg patch 初探 微博deviceId 案例

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向过程 看了b站迷人瑞信那个由于是…

循序渐进 Android Binder(一):IPC 基本概念和 AIDL 跨进程通信的简单实例

Binder 给人的第一印象是”捆绑者“&#xff0c;即将两个需要建立关系的事物用某些工具束缚在一起。在 Android 中&#xff0c;Binder 是一种高效的跨进程通信&#xff08;IPC&#xff09;机制&#xff0c;它将可以将运行在不同进程中的组件进行绑定&#xff0c;以实现彼此通信…

网络安全之Web渗透加解密

项目基本使用 准备环境&#xff1a;node.js python chrome npm install chrome-remote-interface pip install playwright playwright install chromium pip install mitmproxy ............... 第一步启动cdp.js。 第二步使用python .\cdp_load.py vue_demo&#xff0c;连…

【LeetCode 题解】两数之和(C++/Python 双解法):从语法到算法的全面解析

【LeetCode题解】两数之和&#xff08;C/Python双解法&#xff09;&#xff1a;从语法到算法的全面解析 一、题目描述 题目链接&#xff1a;1. 两数之和 难度&#xff1a;简单 要求&#xff1a;给定一个整数数组 nums 和一个整数目标值 target&#xff0c;在数组中找出两个数…

【机器学习基础】机器学习入门核心算法:集成学习(Ensemble Learning)

机器学习入门核心算法&#xff1a;集成学习&#xff08;Ensemble Learning&#xff09; 1. 算法逻辑核心逻辑&#xff1a; 2. 算法原理与数学推导2.1 Bagging&#xff08;Bootstrap Aggregating&#xff09;2.2 Boosting2.3 Stacking 3. 模型评估评估指标基学习器选择策略 4. 应…

【TMS570LC4357】之相关驱动开发学习记录1

系列文章目录 【TMS570LC4357】之工程创建 【TMS570LC4357】之工程配置修改 【TMS570LC4357】之HALCOGEN使用 【TMS570LC4357】之相关问题及解决 ——————————————————— 前言 记录笔者在第一次使用TMS570过程中对外设驱动的一些学习碎片。 1. RTI 1.1 添…

RAG入门 - Retriever(1)

文章目录 环境准备知识库加载1. Retriever - embeddings &#x1f5c2;️1.1 将文档拆分为chunks1.2 词嵌入1.3 构建向量数据库Nearest Neighbor search algorithm &#xff08;最近邻搜索算法&#xff09;Distances &#xff08;距离&#xff09;点积&#xff08;Dot Product&…

pyspark实践

1。pyspark是什么 PySpark 是 Apache Spark 的官方 Python 接口&#xff0c;它使得 Python 开发者能够访问 Spark 的核心功能&#xff0c;如&#xff1a; Spark SQL&#xff1a;用于执行 SQL 查询以及读取数据的库&#xff0c;支持多种数据格式和存储系统。py.qizhen.xyz Data…

【深度学习新浪潮】多模态模型如何处理任意分辨率输入?

多模态模型处理任意分辨率输入的能力主要依赖于架构设计的灵活性和预处理技术的结合。以下是核心方法及技术细节: 一、图像模态的分辨率处理 1. 基于Transformer的可变补丁划分(ViT架构) 补丁化(Patch Embedding): 将图像分割为固定大小的补丁(如1616或3232像素),不…

K-匿名模型

K-匿名模型是隐私保护领域的一项基础技术&#xff0c;防止通过链接攻击从公开数据中重新识别特定个体。其核心思想是让每个个体在发布的数据中“隐匿于人群”&#xff0c;确保任意一条记录至少与其他K-1条记录在准标识符&#xff08;Quasi-Identifiers, QIDs&#xff09;上不可…

UE5蓝图暴露变量,在游戏运行时修改变量实时变化、看向目标跟随目标Find Look at Rotation、修改玩家自身弹簧臂

UE5蓝图中暴露变量&#xff0c;类似Unity中public一个变量&#xff0c;在游戏运行时修改变量实时变化 1&#xff0c;添加变量 2&#xff0c;设置变量的值 3&#xff0c;点开小眼睛&#xff0c;此变量显示在编辑器中&#xff0c;可以运行时修改 看向目标跟随目标Find Look at R…

Python-matplotlib中的Pyplot API和面向对象 API

matplotlib中的Pyplot API和面向对象 API Pyplot API&#xff08;状态机模式&#xff09;面向对象 API 详解二者差别核心区别方法命名差异注意事项差别举例 &#x1f345; Pyplot API&#xff08;状态机模式&#xff09;和面向对象 API 是两种不同的编程接口.&#x1f345; 它们…

FastAPI安全认证:从密码到令牌的魔法之旅

title: FastAPI安全认证:从密码到令牌的魔法之旅 date: 2025/06/02 13:24:43 updated: 2025/06/02 13:24:43 author: cmdragon excerpt: 在FastAPI中实现OAuth2密码流程的认证机制。通过创建令牌端点,用户可以使用用户名和密码获取JWT访问令牌。代码示例展示了如何使用Cry…

java对接bacnet ip协议(跨网段方式)

1、环境准备 #maven环境<repositories><repository><id>ias-releases</id><url>https://maven.mangoautomation.net/repository/ias-release/</url></repository></repositories><dependencies><dependency><…

LabVIEW超宽带紧凑场测量系统

采用 LabVIEW 开发超宽带紧凑场测量系统&#xff0c;实现天线方向图、目标雷达散射截面&#xff08;RCS&#xff09;及天线增益的自动化测量。通过品牌硬件设备&#xff0c;优化系统架构&#xff0c;解决传统测量系统在兼容性、数据处理效率及操作便捷性等方面的问题&#xff0…

编译rustdesk,使用flutter、hwcodec硬件编解码、支持Windows 7系统

目录 安装相应的环境安装visual studio安装vpkg安装rust开发环境安装llvm和clang编译源码下载源码使用Sciter作为UI的(已弃用)使用flutter作为UI的(主流)下载flutter sdk桥接静默安装支持Windows 7系统最近某desk免费的限制越来越多,实在没办法,平时远程控制用的比较多,…

ROS机器人和NPU的往事和新知-250602

往事&#xff1a; 回顾一篇五年前的博客&#xff1a; ROS2机器人笔记20-12-04_ros2 移植到vxworks-CSDN博客 里面提及专用的机器人处理器&#xff0c;那时候只有那么1-2款专用机器人处理器。 无关&#xff1a; 01&#xff1a; 每代人的智商和注意力差异是如何出现的-250602-…

【从零开始学习QT】信号和槽

目录 一、信号和槽概述 信号的本质 槽的本质 二、信号和槽的使用 2.1 连接信号和槽 2.2 查看内置信号和槽 2.3 通过 Qt Creator 生成信号槽代码 自定义槽函数 自定义信号 自定义信号和槽 2.4 带参数的信号和槽 三、信号与槽的连接方式 3.1 一对一 &#xff08;1&…