介绍

在项目升级过程中,遇到了Oracle驱动包升级后,对于Clob字段不再返回String字符串类型了。项目中有人大量使用字符串来处理返回结果,所以需要自定义处理。

在一些特殊情况下,也需要自定义Mybatis对于字段类型的特殊处理。这里举例Oracle的Timestamp类型处理。

处理方式

有几种方式,基本上都是在SqlSessionFactory初始化时,指定字段类型对应的typeHandler处理器。我选择通过MyBatis配置方式,直接扫描typeHandlers的包,这样不用一个个指定了。

mybatis依赖:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>${mybatis.spring.boot.starter}</version>
</dependency>
<!-- 翻页插件,自动配置 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>${pagehelper.version}</version>
</dependency>
<!-- oracle驱动 -->
<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc11</artifactId>
</dependency>
<dependency>
    <groupId>com.oracle.database.nls</groupId>
    <artifactId>orai18n</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

在我们application.properties文件中配置mybatis.type-handlers-package=com.xxx.xxx,com.xxx.xxxtypeHandlers的包名

#mybatis的配置文件 https://mybatis.org/mybatis-3/zh/configuration.html#settings
#实体类别名
#mybatis.type-aliases-package=cn.xxxx
mybatis.mapper-locations= classpath*:mappers/mapper/**/*.xml
#自定义类型处理
mybatis.type-handlers-package=cn.shhuifu.quickframework.boot.datasource.typeHandler
#开启二级缓存,一般不开启
mybatis.configuration.cache-enabled=true
#设置一级缓存作用域sqlSession,  STATEMENT是每次查询等于没一级缓存。先查询二级再查询一级
mybatis.configuration.local-cache-scope=session
#是否开启驼峰自动映射
mybatis.configuration.map-underscore-to-camel-case=true
#控制台出入sql日志
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#枚举处理方法
mybatis.configuration.default-enum-type-handler=org.apache.ibatis.type.EnumOrdinalTypeHandler
#当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
mybatis.configuration.jdbc-type-for-null=null
#指定当结果集中值为 null 的时候是否调用映射对象的 setter方法,解决某些字段null就不返回字段的情况
mybatis.configuration.call-setters-on-nulls=true

自定义typeHandler处理类

package cn.shhuifu.quickframework.boot.datasource.typeHandler;

import oracle.sql.TIMESTAMP;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.*;

/**
 * @ClassName OracleTimestampHandler
 * @Description TIMESTAMP自动转换类型
 * @Author LiuQian
 * @Date 2022-11-13
 **/
@MappedJdbcTypes(JdbcType.TIMESTAMP)
@MappedTypes(TIMESTAMP.class)
public class OracleTimestampHandler extends BaseTypeHandler<Timestamp> {

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Timestamp timestamp, JdbcType jdbcType) throws SQLException {
        preparedStatement.setTimestamp(i, timestamp);
    }

    @Override
    public Timestamp getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return parse2time(resultSet.getObject(s));
    }

    @Override
    public Timestamp getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return parse2time(resultSet.getObject(i));
    }

    @Override
    public Timestamp getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return parse2time(callableStatement.getObject(i));
    }

    private Timestamp parse2time(Object value) {
        if (value instanceof Date) {
            return new Timestamp(((Date) value).getTime());
        } else if (value instanceof Time) {
            return new Timestamp(((Time) value).getTime());
        } else if (value instanceof Timestamp) {
            return (Timestamp) value;
        } else if(value instanceof TIMESTAMP){
            try {
                return ((TIMESTAMP)value).timestampValue();
            } catch (SQLException throwables) {
                return null;
            }
        }
        return null;
    }
}
package cn.shhuifu.quickframework.boot.datasource.typeHandler;

import oracle.jdbc.OracleClob;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.io.StringReader;
import java.sql.*;

/**
 * @ClassName OracleClobHandler
 * @Description
 * @Author LiuQian
 * @Date 2022-11-15
 **/
@MappedJdbcTypes(JdbcType.CLOB)
@MappedTypes(OracleClob.class)
public class OracleClobHandler extends BaseTypeHandler<String> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        StringReader reader = new StringReader(parameter);
        ps.setCharacterStream(i, reader, parameter.length());
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Clob clob = rs.getClob(columnName);
        return this.toString(clob);
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Clob clob = rs.getClob(columnIndex);
        return this.toString(clob);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Clob clob = cs.getClob(columnIndex);
        return this.toString(clob);
    }

    private String toString(Clob clob) throws SQLException {
        return clob == null ? null : clob.getSubString(1L, (int)clob.length());
    }
}

在哪里配置的?

其实主要通过MybatisAutoConfiguration自动配置类的方式让SqlSessionFactory在初始化的时候指定包路径,该类内部会引用环境变量MybatisProperties

1.在配置文件中配置mybatis.type-handlers-package=cn.xxxtypeHandlers

2.MybatisAutoConfiguration中定义sqlsessionFactory时会使用typeHandlersPackage.

3.在sqlSessionFactoryBeanafterPropertiesSet()后置处理方法中进行register注册

@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
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;
    private final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers;

    //。。。省略

    @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);
        }

        this.applySqlSessionFactoryBeanCustomizers(factory);
        return factory.getObject();
    }

   //。。。省略
} 

文章作者: 刘同学
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 刘同学的小站
数据库 Mybatis
喜欢就支持一下吧