Mybatis自定义数据类型处理TypeHandler
介绍
在项目升级过程中,遇到了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.xxx是typeHandlers的包名
#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.在sqlSessionFactoryBean的afterPropertiesSet()后置处理方法中进行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();
}
//。。。省略
}
本文链接:
/archives/1705885829469
版权声明:
本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
刘同学的小站!
喜欢就支持一下吧