随记1 MySQL之特殊字符(表情)的存储以及读取乱码问题

MySQL之特殊字符(表情)的存储以及读取乱码问题

特殊字符的乱码问题是程序开发以及软件应用中很常见的问题,根据不同场景也有不同的原因。本篇仅记录特定情境下遇到的问题与解决方法,MySQL存储带有表情等特殊字符的不规则数据的应用场景。

1. 从讲故事开始
1.1 字与字节与字符
  1. 字节(Byte)是计算机存储容量基本单位,8个二进制位组成1个字节。
  2. 字是计算机一次运算能处理的位数,如32位机器中字=4字节
  3. 字符是一种符号,肉眼可见的汉字,字母等

字符与字节的关系:一般情况下,一个标准英文字母或数字占一个字节位置,一个标准汉字占二个字节位置表示,在不同的编码方式下一个字符占的字节不太一样。

1.2 不同编码集
  1. ASCII(ISO-8859-1)是鼻祖,最简单的方式
  2. B2312、GBK、GB18030,GBK系列(递增向下兼容)是双字节中文编码方式,包含全部中文字符。不论中、英文字符均使用双字节来表示
  3. UTF-8编码则是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码
  4. Unicode是统一编码,它建立了一个全世界统一的码表。世界上的所有文字,在这张码表中都是唯一的。UTF-8, UTF-16, UTF-32 就是 Unicode 不同的实现。
2. 特殊字符(表情)的存储

Emoji 是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上.utf8编码最大字符长度为 3 字节,无法解析Emoji。
MySQL在5.5.3之后增加了这个utf8mb4的编码,专门用来兼容四字节的unicode

  1. 首先存储建表时,要采用字段级或者表级utf8mb4编码格式。
	CHARSET=utf8mb4 
  1. 含有Emoji的字段存储为BLOB大字段类型

utf8mb4解决了某些Emoji无法解析(一个字符占四个字节)的问题,还有一些特殊Emoji字符其实是两个字符(下图),要使用BLOB才能存储,尝试过longtext类型也无法存储
pic

3. 特殊字符(表情)的读取

由于字段类型是BLOB,对应的Java实体类类型应该为byte[],若依旧采用String类型,出现了Emoji乱码的问题,由于持久化框架采用mybatis框架。则解决思路是在mybatis里读取时,利用byte[]转化。
在mapper.xml中的字段里,配置处理的工具类

package xx.xx.xx.xx.util;

import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

/**
 * 自定义typehandler,解决mybatis存储blob字段后,出现乱码的问题
 * 在mapper.xml中配置:
 * 将result标签中的jdbcType属性改为typeHandler属性即可,如下 
 * 
 */

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 {  
            //###把String转化成byte流  
            bis = new ByteArrayInputStream(parameter.getBytes(DEFAULT_CHARSET));  
        } catch (UnsupportedEncodingException e) {  
            throw new RuntimeException("Blob Encoding Error!");  
        }     
        ps.setBinaryStream(i, bis, parameter.length());  
    }  

    @Override  
    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 {  
            //###把byte转化成string  
            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;
	}  
	
} 

你可能感兴趣的:(数据库,mysql,乱码,mybatis)