You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Oracle中存Java对象流的RAW列转换为VARCHAR2明文

解决Oracle中RAW存储Java序列化国家代码转明文VARCHAR2的问题

问题背景

项目里用Oracle的RAW类型存储Java序列化后的国家代码列表,比如['AW']会被存成0xACED0005757200135B4C6A6176612E6C616E672E537472696E673BADD256E7E91D7B470200007870000000017400024157。这种方案有两个明显问题:

  • 调试时看数据库内容完全不直观,没法直接知道存的是哪些国家代码
  • 后续写SQL脚本操作、比较该列时,RAW类型限制太多,不好处理

尝试过直接用UPDATE语句修改失败,因为RAW类型不能直接和字符串用=匹配,比如这条语句根本执行不了:

UPDATE my_table
SET country_codes = '[AW]'
WHERE country_codes = '0xACED0005757200135B4C6A6176612E6C616E672E537472696E673BADD256E7E91D7B470200007870000000017400024157';

需求很明确:把该列改成存储明文字符串(比如VARCHAR2类型),后续会同步调整应用逻辑适配新格式。

转换前数据

idcountry_codes [RAW]
10xACED0005757200135B4C6A6176612E6C616E672E537472696E673BADD256E7E91D7B470200007870000000017400024157
20xACED0005757200135B4C6A6176612E6C616E672E537472696E673BADD256E7E91D7B47020000787000000001740002444B
30xACED0005757200135B4C6A6176612E6C616E672E537472696E673BADD256E7E91D7B47020000787000000002740002444B7400025553

目标转换后数据

idcountry_codes [VARCHAR2(255)]
1[AW]
2[DK]
3[DK, US]

分步解决方法

1. 新增临时列存明文

先别直接改原列,避免数据丢失,先加一个VARCHAR2类型的临时列:

ALTER TABLE my_table ADD country_codes_txt VARCHAR2(255);

2. 写函数解析RAW序列化数据

Java序列化的RAW数据开头是固定魔数0xACED0005,后面跟着对象类型标识和实际字符串内容。可以写个PL/SQL函数把这些RAW数据转成明文列表:

CREATE OR REPLACE FUNCTION deserialize_country_codes(p_raw RAW) RETURN VARCHAR2 IS
    v_buffer RAW(32767);
    v_offset NUMBER := 1;
    v_list_size NUMBER;
    v_str_len NUMBER;
    v_str VARCHAR2(255);
    v_result VARCHAR2(255) := '[';
BEGIN
    -- 跳过序列化魔数和对象头(前28字节,对应示例里的固定前缀)
    v_offset := 29;
    
    -- 读取列表里的元素个数(4字节无符号整数)
    v_buffer := UTL_RAW.SUBSTR(p_raw, v_offset, 4);
    v_list_size := UTL_RAW.CAST_TO_NUMBER(v_buffer);
    v_offset := v_offset + 4;
    
    FOR i IN 1..v_list_size LOOP
        -- 跳过字符串标识字节('t'对应的十六进制74)
        v_offset := v_offset + 1;
        -- 读取字符串长度(2字节)
        v_buffer := UTL_RAW.SUBSTR(p_raw, v_offset, 2);
        v_str_len := UTL_RAW.CAST_TO_NUMBER(v_buffer);
        v_offset := v_offset + 2;
        -- 读取字符串内容(Java序列化字符串是UTF-16BE,每个字符占2字节)
        v_str := UTL_RAW.CAST_TO_VARCHAR2(UTL_RAW.SUBSTR(p_raw, v_offset, v_str_len * 2));
        v_offset := v_offset + v_str_len * 2;
        
        -- 拼接成目标格式
        IF i > 1 THEN
            v_result := v_result || ', ';
        END IF;
        v_result := v_result || v_str;
    END LOOP;
    
    v_result := v_result || ']';
    RETURN v_result;
EXCEPTION
    WHEN OTHERS THEN
        RETURN '[]'; -- 解析失败返回空列表格式,避免报错
END;
/

3. 更新临时列数据

调用上面的函数,把原RAW列的数据转成明文存入临时列:

UPDATE my_table SET country_codes_txt = deserialize_country_codes(country_codes);
COMMIT;

4. 验证转换结果

查一下数据,确认转换后的内容符合预期:

SELECT id, country_codes, country_codes_txt FROM my_table;

5. 替换原列(可选)

如果验证没问题,就可以替换原列了:

  • 先把原RAW列重命名做备份:
ALTER TABLE my_table RENAME COLUMN country_codes TO country_codes_raw;
  • 再把临时列改成原列名:
ALTER TABLE my_table RENAME COLUMN country_codes_txt TO country_codes;

6. 清理备份列(可选)

如果不需要保留原RAW数据了,可以删除备份列:

ALTER TABLE my_table DROP COLUMN country_codes_raw;

关键注意点

  • UTL_RAW.CAST_TO_VARCHAR2()负责把RAW字节转成字符串,但要注意Java序列化的字符串是UTF-16BE编码,每个字符占2字节,所以解析时长度要对应
  • 函数里的偏移量是针对你提供的示例数据结构调的,如果Java序列化配置或版本变了,可能需要微调偏移量
  • 转换前务必全量备份数据,避免意外丢失

内容的提问来源于stack exchange,提问作者Nermin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:35:54