读取数据库Blob类型的文本数据

开发一个查询功能时,遇到了一个ORM的问题:数据库字段是 Blob 类型,里面实际存储的是文本数据,Java 后端代码中用字符串 String 类型去接收这个字段的数据时,报错,提示没有对应的setter方法,类型不匹配;换成 byte[] 字节数组类型去接收这个字段的数据,依然报错,一样是找不到setter方法,类型不匹配;最后只好将Java中对应的变量类型改成 java.sql.Blob 类型去接收对应的数据,不报错了,但如何取获取其中的文本数据呢?java

使用的代码以下:sql

private String getTextFromBlob(Blob blob) {
    int i = 1;
    byte btArr[] = new byte[0];
    try {
        while (i < blob.length()) {
            byte[] bytes = blob.getBytes(i, 1024);
            btArr = ArrayUtils.addAll(btArr, bytes);
            i += 1024;
        }
        return new String(btArr, "GB2312");
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        return null;
    }
}

代码逻辑并不复杂,但其实这地方有一个坑,须要注意一下。数据库

最开始的时候写的代码并非这样,我在 while 循环里每次拿 1024 个字节的数据,而后使用 new  String(bytes,"GB2312") 获得字符串,再用 StringBuilder 把每次循环获得的字符串拼接起来,最后 stringBuilder.toString() 返回完整的字符串内容。apache

写完以后,测试,没问题。但紧接着,我想到了一个问题:我每次拿1024个字节,会不会正好把组成一个汉字的两个字节拆分开呢?后端

答案是确定的。虽然 GB2312 编码字符集固定使用 2 个字节来存储汉字,可是 GB2312字符集在存储 ASCII 字符的时候,用的是 1 个字节来存储。也就是说,对于英文字母、数字、英文标点,GB2312 用一个字节存储;对于中文,则使用两个字节存储。这样的话就无法保证每次拿1024个字节不会把某个汉字的两个字节拆分红两段。数组

由于数据库中的文字都比较短,没有超过一百个字的,程序每次拿1024个字节就把全部的内容都拿完了,因此在测试中返回的文本都是正常的。改了一下代码,把 1024 改为 10,每次拿10个字节,果真出现了乱码问题,文本中的部分中文出现了乱码,而其余部分的中文是正常的。性能

最后改为了上面的代码,每次依然拿固定长度的字节,而后把结果都放到一个 byte[] 字节数组里,等拿完全部的字节以后,使用 new  String(bytes,"GB2312") 获得字符串,这样就避免了上面的问题。实际测试以后(每次拿10个字节),返回的文本正常,没有乱码。测试

固然这个代码并不完美,代码里使用 apache collections 包里的 ArrayUtils.addAll(byte[] b,byte[] c) 方法来把两个字节数据拼到一块,其内部的实现方法就是建立一个大数组,而后把两个数据的内容依次放进去,这样的话每次都要开辟一个新的内存空间,效率并不高,若是数据量大的话,会有很大的性能开销。ui

一个比较好的解决方案就是:本身定义一个大数组,每次循环把取到的内容放到这个大数组对应的位置上,避免每次都要 new 一个数组出来,性能更好。缺点就是代码逻辑会复杂一些。编码

 

总结:

在对字节流进行读取、拆分的时候,须要注意会不会把表示一个字符的几个字节给误拆分了,这样最后获得的内容会有部分乱码。像常见的GB23十二、UTF-八、UTF-16等都是变长的方式进行字节存储,不能进行拆分;而像 UCS-2 这样的字符集,固定使用两个字节存储,按偶数进行拆分就没问题。

相关文章
相关标签/搜索