Mybatis結合springboot進行編碼設置

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,通過反射創建該對象。如果自行設置的話,他會找自行設置的配置類

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章