Mybatis結合springboot進行編碼設置
1.兩種方式
業務背景:
數據庫表結構導出,頁面進行動態數據源提供,然後也提供編碼,防止中文亂碼
1)Filter
doFilter()時候對response進行統一設置編碼
但是因爲我這個是動態編碼,只是response得先解碼然後再
2)mybatis中實現typeHandler接口
主要是對xml中的typeHandler
mapper的xml 如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xs.database.mapper.DataBaseTableMapper">
<!--加上二級緩存,雖然大材小用 哈哈,一級緩存就可以,因爲我就寫了這一個mapper的,
二級緩存使用在namespace區域-->
<!--<cache/>-->
<resultMap id="dbMapOracle" type="com.xs.database.entity.DBTableEntity"
>
<id column="tableName" property="tableName" jdbcType="VARCHAR"></id>
<result column="tableNameCn" property="tableNameCn" jdbcType="VARCHAR"
typeHandler="com.xs.database.handler.BatisEncodeHandler"></result>
<collection property="columnEntity" resultMap="colMapOracle">
</collection>
</resultMap>
<resultMap id="colMapOracle" type="com.xs.database.entity.ColumnEntity">
<id column="columnName" property="columnName" jdbcType="VARCHAR"></id>
<result column="tableNameC" property="tableName" jdbcType="VARCHAR"></result>
<result column="columnNameCn" property="columnNameCn" jdbcType="VARCHAR"
typeHandler="com.xs.database.handler.BatisEncodeHandler"></result>
<result column="dataType" property="dataType" jdbcType="VARCHAR"></result>
<result column="nullAble" property="nullAble" jdbcType="VARCHAR"></result>
<result column="dataLength" property="dataLength" jdbcType="INTEGER"></result>
<!--oracle中long字段處理-->
<result column="defaultData" property="defaultData"
jdbcType="LONGVARBINARY"></result>
</resultMap>
<resultMap id="dbMap" type="com.xs.database.entity.DBTableEntity">
<id column="tableName" property="tableName" jdbcType="VARCHAR"></id>
<result column="tableNameCn" property="tableNameCn" jdbcType="VARCHAR"
typeHandler="com.xs.database.handler.BatisEncodeHandler"></result>
<collection property="columnEntity" resultMap="colMap">
</collection>
</resultMap>
<resultMap id="colMap" type="com.xs.database.entity.ColumnEntity">
<id column="columnName" property="columnName" jdbcType="VARCHAR"></id>
<result column="tableNameC" property="tableName" jdbcType="VARCHAR"></result>
<result column="columnNameCn" property="columnNameCn" jdbcType="VARCHAR"
typeHandler="com.xs.database.handler.BatisEncodeHandler"></result>
<result column="dataType" property="dataType" jdbcType="VARCHAR"></result>
<result column="nullAble" property="nullAble" jdbcType="VARCHAR"></result>
<result column="dataLength" property="dataLength" jdbcType="INTEGER"></result>
<result column="defaultData" property="defaultData" jdbcType="VARCHAR"></result>
</resultMap>
<!--###################################################################-->
<select id="getTableListByOracle" resultMap="dbMapOracle">
SELECT Ss.TABLE_NAME as "tableName",
T.COMMENTS as "tableNameCn",
s.table_name as "tableNameC",
s.COLUMN_NAME as "columnName",
c.comments as "columnNameCn",
s.DATA_TYPE as "dataType",
s.NULLABLE as "nullAble",
s.DATA_LENGTH as "dataLength", --number
s.DATA_DEFAULT as "defaultData"--long
FROM USER_TABLES Ss
left join USER_TAB_COMMENTS T
on Ss.TABLE_NAME = T.TABLE_NAME
left join user_tab_columns S
on s.TABLE_NAME = Ss.TABLE_NAME
left join user_col_comments c
on c.table_name = s.TABLE_NAME
and c.column_name = s.COLUMN_NAME
<where>
t.table_type = 'TABLE'
<if test="tableName != null and tableName!=''">
and ss.table_name like #{tableName}
</if>
</where>
</select>
<select id="getTableListByMysql" resultMap="dbMap">
SELECT
t.table_name AS "tableName",
t.table_comment AS "tableNameCn",
c.column_name AS "columnName",
c.column_default AS "defaultData",
c.is_nullable AS "nullAble",
c.data_type AS "dataType",
c.character_maximum_length AS "dataLength",
c.column_comment AS "columnNameCn"
FROM
information_schema.TABLES t
LEFT JOIN information_schema.COLUMNS c ON t.table_name = c.table_name
AND t.table_schema = c.table_schema
<where>
t.table_schema = (select database())
<if test="tableName != null and tableName!=''">
AND t.table_name like #{tableName}
</if>
</where>
ORDER BY
c.ordinal_position
</select>
<select id="testCon" resultType="int" useCache="false" flushCache="true">
select 1 from dual
</select>
</mapper>
實現接口,getResult()對返回的值,進行重新編碼
package com.xs.database.handler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @Author xueshuai
* @Date 2019/4/22 15:18
* @Description 中文亂碼處理handler,實現mybatis的typeHandler接口
* 數據庫編碼:
* 解數據庫編碼 重新編碼
* ISO-8559-1 GBK
* GBK/GB2312 GBK
* unicode UTF-8/UTF-16 不變
*
*/
@Component
public class BatisEncodeHandler implements TypeHandler<String> {
private String code;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public BatisEncodeHandler(String code) {
this.code = code;
}
public BatisEncodeHandler() {
System.out.println("創建"+this+" bean============");
}
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
}
/**
* 進行重新編碼,解決中文亂碼問題
* @param rs
* @param columnName
* @return
* @throws SQLException
*/
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
String string = rs.getString(columnName);
try {
if(string!=null && this.code != null){
if("UTF-8".equals(this.code)){
return string;
}else{
byte[] bytes = string.getBytes(this.code);
return new String(bytes,"GBK");
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return string;
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
String string = rs.getString(columnIndex);
return string;
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}
因爲我這邊都是動態數據源,所以整個的sqlSessionFactory是自己來創建的,並且通過sqlSessionFactoryBean進行整個mybatis的設置。(之前很傻逼的做了很多無用功,瘋狂直接在typeHandlerRegistry自己來設置,這樣的錯誤,會創建兩個對象,後面我自己動態設置編碼就不起作用了,稍後附上源碼流程)
package com.xs.database.config;
import com.xs.database.handler.BatisEncodeHandler;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.LocalCacheScope;
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.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.core.io.DefaultResourceLoader;
import javax.sql.DataSource;
/**
* @Author xueshuai
* @Date 2019/4/27 14:53
* @Description DynamicDataSource配置類註冊到spring容器中去
*/
@Configuration
@DependsOn("dataBaseTableController")
@MapperScan(basePackages="com.xs.database.mapper",sqlSessionFactoryRef = "sqlSessionFactory")
public class DynamicDataSourceConfig{
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.driver-class-name}")
private String driver;
@Value("${spring.datasource.password}")
private String password;
@Autowired
BatisEncodeHandler batisEncodeHandler;
/**/
@Bean
public DynamicDataSource dynamicDataSource(){
DynamicDataSource dataSource = new DynamicDataSource();
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setPassword(password);
hikariConfig.setJdbcUrl(url);
hikariConfig.setUsername(userName);
hikariConfig.setDriverClassName(driver);
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
dataSource.map.put("defaultDatasource",
hikariDataSource);
//繼承abstractRoutingDataSource類初始化bean會驗證targetDataSource 不爲空
dataSource.setTargetDataSources(dataSource.map);
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
org.apache.ibatis.session.Configuration configuration =
new org.apache.ibatis.session.Configuration();
LocalCacheScope localCacheScope = LocalCacheScope.STATEMENT;
configuration.setLocalCacheScope(localCacheScope);
configuration.setCallSettersOnNulls(true);
configuration.setCacheEnabled(false);
configuration.setMapUnderscoreToCamelCase(true);
//1.mybatis自動導入sqlsessionfactory時候,會註冊一個handler,後面resultMap
// 解析的時候,會驗證是否存在typeHandler
//如果存在,則不創建,不存在,會解析xml中的所有typeHandler(這樣會導致創建多個typeHandler對象)
// TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
// typeHandlerRegistry.register(batisEncodeHandler.getClass());
factory.setTypeHandlers(batisEncodeHandler);
factory.setConfiguration(configuration);
factory.setMapperLocations(new DefaultResourceLoader(this.getClass().getClassLoader()).getResource("static/xml/DataBaseTableMapper.xml"));
return factory.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
}
用法:
package com.xs.database.serivce.impl;
import com.alibaba.druid.pool.DruidDataSource;
import com.xs.database.entity.DBTableEntity;
import com.xs.database.config.CustomException;
import com.xs.database.config.DynamicDataSource;
import com.xs.database.config.ResultCode;
import com.xs.database.entity.ConnectionEntity;
import com.xs.database.handler.BatisEncodeHandler;
import com.xs.database.mapper.DataBaseTableMapper;
import com.xs.database.serivce.IDataBaseTableService;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import oracle.jdbc.driver.OracleDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.sql.Driver;
import java.util.List;
/**
* @Author xueshuai
* @Date 2019/4/10 13:30
* @Description
*/
@Service("MybatisTableService")
public class MybatisTableService implements IDataBaseTableService {
@Autowired
DataBaseTableMapper mapper;
@Autowired
BatisEncodeHandler batisEncodeHandler;
private Logger logger = LoggerFactory.getLogger(MybatisTableService.class);
@Override
public List<DBTableEntity> getTableList(ConnectionEntity conEntity) throws Exception {
try {
reSetDB(conEntity);
} catch (Exception e) {
logger.error(e.getMessage());
throw new CustomException(ResultCode.FAILED.getCode(), "數據庫配置錯誤,請檢查數據庫信息!");
}
//中文亂碼處理
batisEncodeHandler.setCode(conEntity.getEncode());
String dbType = conEntity.getDataType();
String tableName = StringUtils.isEmpty(conEntity.getTableName())?"":
"%" + conEntity.getTableName().toUpperCase() +
"%";
List<DBTableEntity> tableList = null;
if ("mysql".equals(dbType)) {
tableList = mapper.getTableListByMysql(tableName);
} else if ("oracle".equals(dbType)) {
tableList = mapper.getTableListByOracle(tableName);
}
return tableList;
}
@Override
public boolean testCon(ConnectionEntity conEntity) {
try {
reSetDB(conEntity);
} catch (Exception e) {
logger.error(e.getMessage());
throw new CustomException(ResultCode.FAILED.getCode(), "數據庫配置錯誤,請檢查數據庫信息!");
}
return mapper.testCon()>0?true:false;
}
private void reSetDB(ConnectionEntity conEntity) throws Exception {
String driver = null;
String url = "";
/*重新獲取DataSource bean,然後設置數據庫信息*/
if ("mysql".equals(conEntity.getDataType())) {
driver = "com.mysql.cj.jdbc.Driver";
url = "jdbc:mysql://" + conEntity.getIp() + ":"
+ conEntity.getPort() + "/" + conEntity.getdataName();
} else if ("oracle".equals(conEntity.getDataType())) {
driver = "oracle.jdbc.driver.OracleDriver";
url = "jdbc:oracle:thin:@" + conEntity.getIp() + ":"
+ conEntity.getPort() + ":" + conEntity.getdataName();
}
try{
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setPassword(conEntity.getPassword());
hikariConfig.setJdbcUrl(url);
hikariConfig.setUsername(conEntity.getUserName());
hikariConfig.setDriverClassName(driver);
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
DynamicDataSource.setDataSource(hikariDataSource);
}catch (Exception e){
throw e;
}
}
}
其實用法也很簡單,主要我們來看看這個源碼流程
第一我們得先知道spring創建bean的過程
springboot結合mybatis時候,如果是默認的,那麼就會自動注入SqlSessionFactory
public class MybatisAutoConfiguration implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final TypeHandler[] typeHandlers;
private final LanguageDriver[] languageDrivers;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
private final List<ConfigurationCustomizer> configurationCustomizers;
public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
this.properties = properties;
this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable();
this.typeHandlers = (TypeHandler[])typeHandlersProvider.getIfAvailable();
this.languageDrivers = (LanguageDriver[])languageDriversProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();
this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();
}
public void afterPropertiesSet() {
this.checkConfigFileExists();
}
private void checkConfigFileExists() {
if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
Assert.state(resource.exists(), "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
}
}
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
this.applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (this.properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
factory.setTypeHandlers(this.typeHandlers);
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
Set<String> factoryPropertyNames = (Set)Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
factory.setScriptingLanguageDrivers(this.languageDrivers);
if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
defaultLanguageDriver = this.languageDrivers[0].getClass();
}
}
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
}
return factory.getObject();
}
//後面註冊的bean和注入就省略了,自行看下
}
我業務的話,自己生產的,然後注入在spring容器中,上文的代碼已經貼出了
那麼當你用到一個controller的時候,他會反射,根據類對象得出構造器,然後進行newInstance()得到對象,然後會遍歷其中的@autowire注入的bean,並同樣的拿到對象,一直從service到mapper,找到sqlSessionFactory類型找sqlSessionFactory。默認的話,會找到MybatisAutoConfiguration,通過反射創建該對象。如果自行設置的話,他會找自行設置的配置類
}
}
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
}
return factory.getObject();
}
//後面註冊的bean和注入就省略了,自行看下
}
我業務的話,自己生產的,然後注入在spring容器中,上文的代碼已經貼出了
那麼當你用到一個controller的時候,他會反射,根據類對象得出構造器,然後進行newInstance()得到對象,然後會遍歷其中的@autowire注入的bean,並同樣的拿到對象,一直從service到mapper,找到sqlSessionFactory類型找sqlSessionFactory。默認的話,會找到MybatisAutoConfiguration,通過反射創建該對象。如果自行設置的話,他會找自行設置的配置類