项目已经在生产环境运行一段时间后,发现部分特殊符号存在乱码问题。经过查找资料后,大约有两种思路解决该问题,一是将字符集更换为UTF-8,二是将存储特殊字符的字段类型改为使用国家字符集中的NVARCHAR2。如前文所说,项目已经上线运行,更换字符集风险过大,于是采用第二种方式。
在调试的时候,遇到了无法批量插入的问题,可参考另一篇拙作解决:
Mybatis+Oracle批量插入方法总结_mybatis oracle批量插入_Chrisf Zhang的博客-优快云博客
下面总结解决问题的两个思路,老规矩,将最终完美解决问题的方法记录在方式一,方式二记录思路及遇到的问题。
方式一:
大致思路:数据库字段类型改为NVARCHAR2,mapper.xml中对应字段指定typeHandler,并为该类型重写typeHandler。
tips:
1 typeHandler是按照名称匹配的,mapper.xml中执行的类型与typeHandler的名称要对应;
2 原有字段直接把类型由“VARCHAR2”改为“NVARCHAR2”会报错,可以新加一个字段类型设置为“NVARCHAR2”,将原字段的数据复制到新字段,并改名称,最后把新字段的名称修改为原字段的即可。
数据库字段如下:
mapper.xml中的修改如下:
typeHandler如下:
package org.jeecg.modules.material.business.demand.handler;//package org.jeecg.modules.material.business.demand.handler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeException;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class NVarcharTypeHandler extends BaseTypeHandler<String> {
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
if(parameter == null) {
if(jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException var7) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " + "Cause: " + var7, var7);
}
} else {
try {
this.setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception var6) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different configuration property. " + "Cause: " + var6, var6);
}
}
}
/**
* 这里使用setNString而不是setString
* @param ps
* @param i
* @param parameter
* @param jdbcType
* @throws SQLException
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setNString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}
方式二:
同样需要把数据库字段类型改为NVARCHAR2,收到参数后首先将特殊符号转换为16进制,存入数据库中时通过函数“utl_raw.cast_to_nvarchar2”进行转换,查询时无需特殊处理。
该方式仅能解决一部分特殊符号,例如ø等,一些没修改之前没乱码的特殊符号反而会产生乱码,例如φ等。
转换16进制代码如下:
/**
* 字符串转换
* @param string
* @return
*/
public String unicodeEncode(String string) {
char[] utfBytes = string.toCharArray();
String unicodeBytes = "";
for (int i = 0; i < utfBytes.length; i++) {
String hexB = Integer.toHexString(utfBytes[i]);
if (hexB.length() <= 2) {
hexB = "00" + hexB;
}
unicodeBytes = unicodeBytes + hexB;
}
return unicodeBytes;
}
mapper.xml中转换函数用法如下: