解决Mybatis3.3中NVARCHAR2生僻字乱码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_30682027/article/details/80940536

解决Mybatis3.3中NVARCHAR2生僻字乱码

在数据库中原来的字符串类型都是用varchar2类型存储,现在发现生僻字乱码,遂改为NVARCHAR2,但是依然生僻字乱码。

注意

  1. 使用的mybatis版本是3.3,不知道再其它版本中是否有同样问题
  2. 这里的乱码和前后端文件等编码无关,即所有中文存入varchar2,只有生僻字乱码,其它正常

解决方法

第一步、数据库的varchar2字段修改为nvarchar2
  因为varchar2和数据库的字符集有关系,而数据库在ZHS16GBK(应该是等同于GBK吧?!)编码下,不包含生僻字;修改为AL32UTF8后mybatis居然把所有从数据库查出的中文显示为乱码了。。。所以只能使用nvarchar2。由此可以总结出:varchar2与数据库字符集有关,而nvarchar2则与数据库字符集无关(可以在varchar2和nvarchar2字段中手动输入生僻字即可看到证据)

第二步、mybatis中原来的jdbcType=VARCHAR修改为jdbcType=NVARCHAR
  如果不修改的话,mybatis的类型处理器,默认会使用StringTypeHandler来处理,StringTypeHandler源码如下,可以看出使用的依然是setString

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.type;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class StringTypeHandler extends BaseTypeHandler<String> {
    public StringTypeHandler() {
    }

    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }

    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

在来看NStringTypeHandler

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.type;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class NStringTypeHandler extends BaseTypeHandler<String> {
    public NStringTypeHandler() {
    }

    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }

    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

看上去和StringTypeHandler一样呀,调试了几个小时才调好,方法是重写NStringTypeHandler,如下第三步
第三步、重写NVARCHAR的类型处理
如下,暂时只处理了set方法(即改为setNString),因为get出来的结果生僻字没问题,所以没有修改。

package com.ufgov.util.mybatis;

import org.apache.ibatis.type.*;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * NVARCHAR汉字生僻字乱码问题。
 * 主要是NStringTypeHandler中,没有setNString()
 */
@MappedJdbcTypes(JdbcType.NVARCHAR)
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);
  }
}

注意,还需要在mybatis-config.xml中配置一下


    <typeHandlers>
        <typeHandler handler="com.ufgov.util.mybatis.NVarcharTypeHandler"/>
    </typeHandlers>

总结

  1. 数据库字符集,utf8比gbk范围更大,gbk无法现在生僻字。
  2. varchar2与数据库字符集有关,nvarchar与数据库字符集无关
  3. 也许mybatis对NVARCHAR的处理有bug,在NStringTypeHandler中居然没有setNString
  4. 由此可见,PreparedStatementsetStringsetNString实现也是不同的,可以看看源码。

猜你喜欢

转载自blog.csdn.net/qq_30682027/article/details/80940536