讀取數據庫Blob類型的文本數據


開發一個查詢功能時,遇到了一個ORM的問題:數據庫字段是 Blob 類型,里面實際存儲的是文本數據,Java 后端代碼中用字符串 String 類型去接收這個字段的數據時,報錯,提示沒有對應的setter方法,類型不匹配;換成 byte[] 字節數組類型去接收這個字段的數據,依然報錯,同樣是找不到setter方法,類型不匹配;最后只好將Java中對應的變量類型改為 java.sql.Blob 類型去接收對應的數據,不報錯了,但如何取獲取其中的文本數據呢?

使用的代碼如下:

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() 返回完整的字符串內容。

寫完之后,測試,沒問題。但緊接着,我想到了一個問題:我每次拿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) 方法來把兩個字節數據拼到一塊,其內部的實現方法就是創建一個大數組,然后把兩個數據的內容依次放進去,這樣的話每次都要開辟一個新的內存空間,效率並不高,如果數據量大的話,會有很大的性能開銷。

一個比較好的解決方案就是:自己定義一個大數組,每次循環把取到的內容放到這個大數組對應的位置上,避免每次都要 new 一個數組出來,性能更好。缺點就是代碼邏輯會復雜一些。

 

總結:

在對字節流進行讀取、拆分的時候,需要注意會不會把表示一個字符的幾個字節給誤拆分了,這樣最后得到的內容會有部分亂碼。像常見的GB2312、UTF-8、UTF-16等都是變長的方式進行字節存儲,不能進行拆分;而像 UCS-2 這樣的字符集,固定使用兩個字節存儲,按偶數進行拆分就沒問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM