起因
mybatis-plus 通過Mapper 查詢資料,對映出來的BLOB欄位中的yml資料中文是亂碼的
---
DefaultValue: ''
Formula: ''
HintContent: ''
HintType: ''
OptionsColor:
å¤ç: ''
å¤ä¿®ä¸: ''
å®æ: ''
æ¥å: ''
æ°å»º: ''
è¯ä»·: ''
转信æ¯ç§: ''
转æ»å¡ç§: ''
转设å¤ç§: ''
OptionsIcon:
å¤ç: ''
å¤ä¿®ä¸: ''
å®æ: ''
æ¥å: ''
æ°å»º: ''
è¯ä»·: ''
转信æ¯ç§: ''
转æ»å¡ç§: ''
转设å¤ç§: ''
PossibleComments:
å¤ç: ''
å¤ä¿®ä¸: ''
å®æ: ''
æ¥å: ''
æ°å»º: ''
è¯ä»·: ''
转信æ¯ç§: ''
转æ»å¡ç§: ''
转设å¤ç§: ''
PossibleValues:
å¤ç: å¤ç
å¤ä¿®ä¸: å¤ä¿®ä¸
å®æ: å®æ
æ¥å: æ¥å
æ°å»º: æ°å»º
è¯ä»·: è¯ä»·
转信æ¯ç§: 转信æ¯ç§
转æ»å¡ç§: 转æ»å¡ç§
转设å¤ç§: 转设å¤ç§
Regex: ''
RegexHint: ''
TreeView: '0'
Unique: '0'
我們看一下正常的資料是長下面這樣的
---
DefaultValue: ''
Formula: ''
HintContent: ''
HintType: ''
OptionsColor:
處理: ''
外修中: ''
完成: ''
接單: ''
新建: ''
評價: ''
轉資訊科: ''
轉總務科: ''
轉裝置科: ''
OptionsIcon:
處理: ''
外修中: ''
完成: ''
接單: ''
新建: ''
評價: ''
轉資訊科: ''
轉總務科: ''
轉裝置科: ''
PossibleComments:
處理: ''
外修中: ''
完成: ''
接單: ''
新建: ''
評價: ''
轉資訊科: ''
轉總務科: ''
轉裝置科: ''
PossibleValues:
處理: 處理
外修中: 外修中
完成: 完成
接單: 接單
新建: 新建
評價: 評價
轉資訊科: 轉資訊科
轉總務科: 轉總務科
轉裝置科: 轉裝置科
Regex: ''
RegexHint: ''
TreeView: '0'
Unique: '0'
在來看看這個欄位在資料庫中儲存的樣子:
排查過程
一開始想到的就是經典的亂碼問題。所以嘗試瞭如下方法
1、url 屬性排查
檢查資料庫 url 連結上有沒有新增 characterEncoding=UTF-8,這裡檢視是沒有問題的,因為用的是nacos,擔心是覆蓋出了問題。我還特意在程式碼中列印出來了。
@Value("${spring.datasource.druid.url}")
String dateURL;
log.info("資料庫連線配置 url 屬性為: "+dateURL);
結果如下:
2、IDEA 編碼排查
排查是否是 IDEA 問題,雖然概率小,但是也要排查一下,排查結果還是沒有問題
排查路徑:Setting--->Editor--->File Encodings
3、mybatis xml檔案排查
這裡主要是看 xml 有時候是手寫或者網上覆制過來的話,也可能會造成亂碼。排查結果也是沒有問題
4、排查資料庫中的資料是否亂碼
這個時候,我們在查詢步驟中基本上都排查完了,現在懷疑是不是插入時,資料庫本身儲存就是亂碼的
直接去資料庫中查詢改欄位,並把資料放到 Notepad++ 或者其他編輯器裡面,可以確定資料庫中存的資料是正常的
5、排查查詢其他中文欄位是否會出現亂碼
直接寫SQL去查詢其他中文欄位,查出來的結果是正常的,這就證明了問題確實是出現在 BLOB 這個欄位裡面了
解決辦法
到這裡,基本上排查的方法都用上了,其實上面的排查過程還是比較消耗時間的,這裡我就不做過多繁瑣的描述了,還有一些其他的排查方式。比如換機器,換配置檔案的等也都一一試過,但是環境這方面的排查,我就不講述了,實在是無聊又耗時間。最後確定問題出現在BLOB型別之後,參考網上的文章做了如下的解決方案。
把查出來的資料,作為位元組陣列,保留最完整的原始性,在把 byte[] 強轉為 UTF-8 的 String 型別。
此時就去String中嘗試查詢是否純在此類。萬幸的是找到了
public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException {
this(bytes, 0, bytes.length, charsetName);
}
最後是使用這個方法實現了轉換
new String( dynamicFieldConfig欄位值 , "utf-8" );
但是我們專案中,很多地方都用到了這個欄位。
此時想到mybatis的結果集攔截器,我們可以在結果集攔截器中對這個欄位進行攔截,並對他做語言轉換處理。
最後實現的效果:
package com.dt.cloud.tools;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.sql.*;
/**
* @Description:
* @author: zch
* @Date: 2022/6/23 16:36
* @Version 1.0
*/
public class ConvertBlobTypeHandler extends BaseTypeHandler<String> {
private static final String DEFAULT_CHARSET = "utf-8";
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
String parameter, JdbcType jdbcType) throws SQLException {
ByteArrayInputStream bis;
try {
bis = new ByteArrayInputStream(parameter.getBytes(DEFAULT_CHARSET));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Blob Encoding Error!");
}
ps.setBinaryStream(i, bis, parameter.length());
}
@Override
//rs 為返回結果集,columnName 就是我們需要處理的欄位名,當我們根據欄位名在返回結果集中找出的這個欄位就是 longblob 型別的資料了
public String getNullableResult(ResultSet rs, String columnName)
throws SQLException {
Blob blob = rs.getBlob(columnName);
byte[] returnValue = null;
if (null != blob) {
returnValue = blob.getBytes(1, (int) blob.length());
}
try {
// 核心程式碼,把結果集攔截下來,並且強制轉換為utf-8格式
return new String(returnValue, DEFAULT_CHARSET);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Blob Encoding Error!");
}
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
Blob blob = cs.getBlob(columnIndex);
byte[] returnValue = null;
if (null != blob) {
returnValue = blob.getBytes(1, (int) blob.length());
}
try {
return new String(returnValue, DEFAULT_CHARSET);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Blob Encoding Error!");
}
}
@Override
public String getNullableResult(ResultSet arg0, int arg1)
throws SQLException {
return null;
}
}
在我們的 mapper 文件中需要攔截的 resultMap 中的欄位中增加一個 typeHandler 型別攔截器,這個 typeHandler 的值就是我們 ConvertBlobTypeHandler 類的地址
最後總結:資料庫中的儲存使用的就是 longblob 型別,這個型別在查詢出來的時候如果不進行處理的話就會出現亂碼問題。簡單的處理方式就是在 Mybatis 查詢資料庫的時候增加一個攔截器,給這個型別的欄位改變一下編碼方式。