记一次数据库为latin1编码查询中文乱码问题处理
一、问题描述:
因项目需求,需要整合多个项目一起,其中一个项目因在创建数据库的时候没有设置编码格式,为默认的latin1编码。这就导致查询遇到中文的时候会出现乱码,需要进行转码操作。
二、问题处理过程(本项目为springboot+mybatis):
1、经过多次验证,发现原来数据库的插入数据格式为gbk,然而数据库编码为latin1格式的。网上给出的方案大部分是备份库,然后重新调整数据库编码格式什么的。实际情况,首先风险不说,大部分情况是不允许这么操作,如涉及到别的项目对这个已经进行过处理,如果改变了编码,也许自己项目没有问题,但也会导致别的用这个库的项目受到影响。
2、设置项
2.1、多数据源配置(注意加粗的地方,然后url中characterEncoding的编码格式根据实际需要设置,如果原来库中是gbk就设置为gbk):
spring:
datasource:
db1:
#jdbc-url: jdbc:mysql://host:3306/db1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
url: jdbc:mysql://host:3306/db1?useUnicode=true&characterEncoding=gbk&serverTimezone=Asia/Shanghai
username: root
password: ***
# 驱动
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
mybatis:
type-aliases-package: com.saas.mall.jz.rmi.dao.admin
#设置连接指定编码配置项
connectionInitSqls: set names latin1
db2:
#jdbc-url: jdbc:mysql://host:3306/db2?useUnicode=true&characterEncoding=gbk&serverTimezone=Asia/Shanghai
url: jdbc:mysql://host:3306/db2?useUnicode=true&characterEncoding=gbk&serverTimezone=Asia/Shanghai
username: root
password: ***
# 驱动
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
mybatis:
type-aliases-package: com.saas.mall.jz.rmi.dao.website
connectionInitSqls: set names latin1
db3:
url: jdbc:mysql://host:3306/db3?useUnicode=true&characterEncoding=gbk&serverTimezone=Asia/Shanghai
username: root
password: ****
# 驱动
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
mybatis:
type-aliases-package: com.saas.mall.jz.rmi.dao.evyun
connectionInitSqls: set names latin1
2.2、 java配置项(多数据源需要配置,单数据源只需要配置文件中配置相应项就可以,下面为其中一个数据源的配置,注意DataSource需要是DruidDataSourceBuilder的,只有druid的数据源connectionInitSqls的配置才有用):
package com.saas.mall.jz.rmi.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
/**
* @Description: admin数据库数据源配置
* @Creater: sunxiang
* @CreateTime: 2020年12月21日 下午 14:10
**/
@Configuration
@MapperScan(basePackages = "com.saas.mall.jz.rmi.dao.admin.mapper", sqlSessionFactoryRef = "adminSqlSessionFactory")
public class DataSourceAdminConfig {
@Bean("adminDataSource")
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource getDb1DataSource(){
//注意是DruidDataSourceBuilder 而不是DataSourceBuilder,后者的话设置无效
return DruidDataSourceBuilder.create().build();
}
@Bean("adminSqlSessionFactory")
public SqlSessionFactory db1SqlSessionFactory(@Qualifier("adminDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:sqlMapper/admin/*.xml"));
return bean.getObject();
}
@Bean("adminSqlSessionTemplate")
public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("adminSqlSessionFactory") SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
}
通过上面的配置,再插入和修改数据的时候在执行具体sql语句之前会将连接编码设置为 latin1格式的,保证与数据库编码一致。重点是配置文件中:connectionInitSqls: set names latin1 这个配置。具体作用自行百度下就知道了。这样就解决了插入数据的问题。
2.3、获取数据:
如果不做任何处理在获取数据的时候也会有乱码,因为查出来的为latin1编码格式。指导思路是对该编码转换,我的想法是:String s = new String(params.getBytes("ISO-8859-1"), "gbk");
上述语句中 params为乱码的字段名。
其中利用了mybatis的拦截器对所有的进行拦截处理:
package com.saas.mall.jz.rmi.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.saas.mall.jz.rmi.util.BeanCopyUtil;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@Component
//拦截Executor类的query方法
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class MybatisInterceptor implements Interceptor {
@SuppressWarnings({"rawtypes", "unchecked"})
public Object intercept(Invocation invocation) throws Throwable {
Object result = invocation.proceed(); //执行请求方法,并将所得结果保存到result中
if (result instanceof ArrayList) {
List list = new ArrayList();
ArrayList resultList = (ArrayList) result;
for (int i = 0; i < resultList.size(); i++) {
Object object = resultList.get(i);
if (object instanceof Integer) {
continue;
} else {
Class<?> aClass = object.getClass();
//对object进行转码,然后重新赋值返回,如果其他类型需要自行根据实际情况进行处理
String s = new String(JSONObject.toJSONString(object).getBytes("ISO-8859-1"), "gbk");
Object object1 = JSONObject.toJavaObject(JSON.parseObject(s), aClass);
list.add(object1);
}
}
return list;
}
return result;
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties arg0) {
}
}
上面的操作就完美实现插入和查询的中文乱码问题。