简介

在上几篇中,介绍了MyBatis3对参数和结果的解析转换,对于常规数据类型,默认的处理已经足够应付了,但日常开发中会有一些特殊的类型,就可以通过TypeHandler来进行处理

示例准备

本篇文中用于调试的测试代码请参考:MyBatis3源码解析(1)探索准备

完整的工程已放到GitHub上:https://github.com/lw1243925457/MybatisDemo/tree/master/example

熟悉使用的可以跳过该部分,直接到源码解析部分

数据模型修改

我们先将原来的String类型变成String数组,存入数据库时,使用逗号进行分割,取出时转成String数组,即

  • Java数据结构:
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {private Long id;private String[] name;
}
  • 存入数据库,已Varchar的方式:1,2,3

自定义TypeHandler编写

我们修改完数据结构后,需要编写一个自定义的TypeHandler,如下:

public class StringArrayTypeHandler extends BaseTypeHandler<String[]> {@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, String[] strings, JdbcType jdbcType)throws SQLException {preparedStatement.setString(i, StringUtils.join(strings, ","));}@Overridepublic String[] getNullableResult(ResultSet resultSet, String s) throws SQLException {return convert(resultSet.getString(s));}@Overridepublic String[] getNullableResult(ResultSet resultSet, int i) throws SQLException {return convert(resultSet.getString(i));}@Overridepublic String[] getNullableResult(CallableStatement callableStatement, int i) throws SQLException {return convert(callableStatement.getString(i));}/*** 将查询值转换为数组** @param value 查询值, String* @return 转换结果, String[]*/private String[] convert(String value) {return StringUtils.isEmpty(value) ? new String[0] : value.split(",");}
}

TypeHandler注册

将我们自定义的TypeHandler进行注册,MyBatis后将对应String【】和Varchar使用此TypeHandler

public class MybatisTest {public static SqlSessionFactory buildSqlSessionFactory() {......Configuration configuration = new Configuration(environment);// 注册TypeHandler,注意需要在添加Mapper之前,因为添加Mapper操作的时候就需要TypeHandler了configuration.getTypeHandlerRegistry().register(String[].class, JdbcType.VARCHAR, StringArrayTypeHandler.class);configuration.addMapper(PersonMapper.class);SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();return builder.build(configuration);}
}

运行

测试结果如下:

Connected to the target VM, address: '127.0.0.1:53557', transport: 'socket'
[Person(id=1, name=[1, 2]), Person(id=2, name=[1, 2])]
Disconnected from the target VM, address: '127.0.0.1:53557', transport: 'socket'

源码解析

参数解析设置

通过前面几篇文章的分析,我们得到设置参数的相关源码如下:

public class DefaultParameterHandler implements ParameterHandler {@Overridepublic void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings != null) {for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping parameterMapping = parameterMappings.get(i);if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional paramsvalue = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}// 获取对应的TypeHandlerTypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {// 调动对应的方法,设置值typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException | SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}}}}}
}

设置值,函数如下:

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {@Overridepublic void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {if (parameter == null) {if (jdbcType == null) {throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");}try {ps.setNull(i, jdbcType.TYPE_CODE);} catch (SQLException e) {throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "+ "Cause: " + e, e);}} else {try {// 调用我们自定义的TypeHandler函数setNonNullParameter(ps, i, parameter, jdbcType);} catch (Exception e) {throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "+ "Try setting a different JdbcType for this parameter or a different configuration property. "+ "Cause: " + e, e);}}}
}

如上所示,我们看到TypeHandler调用的大致流程

其中MyBatis3是根据Java Type 和 JDBC Type 来对应调用不同的TypeHandler的,在示例代码中我们也显式的注册了 String数组和Varchar

这里使用的是一个全局的,也可以单独在查询中使用,可以自行搜索

结果解析返回

通过之前的文章,我们知道代码如下:

  private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)throws SQLException {if (propertyMapping.getNestedQueryId() != null) {return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);} else if (propertyMapping.getResultSet() != null) {addPendingChildRelation(rs, metaResultObject, propertyMapping);   // TODO is that OK?return DEFERRED;} else {// 得到对应的TypeHandler,直接返回处理结果final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);return typeHandler.getResult(rs, column);}}

GetResult函数如下,后面调用我们自定义的TypeHandler方法

  @Overridepublic T getResult(ResultSet rs, String columnName) throws SQLException {try {return getNullableResult(rs, columnName);} catch (Exception e) {throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);}}

总结

本篇文章,展示了如何定义TypeHandler和使用TypeHandler,并展示了源码部分,TypeHandler是如何对应生效的,了解该部分,在日常开发中,使用TypeHandler也能做到心中有数

MyBatis3源码解析(6)TypeHandler使用相关推荐

  1. MyBatis3源码解析(7)TypeHandler注册与获取

    简介 在上篇文章中,我们介绍了TypeHandler的简单使用和解析了TypeHandler的处理核心,这篇文章中我们接着看到TypeHandler是如注册和获取使用的 源码解析 TypeHandle ...

  2. Mybatis3 源码解析系列

    简介 Mybatis作为一个优秀的Java持久化框架,在我们的日常工作中相信都会用到,本次源码解析系列,就开始探索下Mybatis 总结 在MyBatis的学习中,首先通读了<MyBatis3源 ...

  3. MyBatis3源码解析(5)查询结果处理

    简介 上篇中解析了MyBatis3中参数是如何传递处理的,本篇接着看看在获取到查询结果后,MyBatis3是如何将SQL查询结果与我们接口函数定义的返回结果对应的 源码 获取结果后处理的入口 在:My ...

  4. MyBatis3源码解析(3)查询语句执行

    简介 上篇探索了MyBatis中如何获取数据库连接,本篇继续探索,来看看MyBatis中如何执行一条查询语句 测试代码 本篇文中用于调试的测试代码请参考:MyBatis3源码解析(1)探索准备 完整的 ...

  5. MyBatis3源码解析(2)数据库连接

    简介 基于上篇的示例感受,下面我们探索下MyBatis连接数据库的细节是如果实现的,在日常使用中是如何能和Druid数据库连接池等配合起来的 源码解析 基于上篇的示例代码: public class ...

  6. MyBatis3源码解析(1)探索准备

    简介 本篇文章将使用原生的JDBC方式操作数据库,然后在使用Mybatis提供的方式操作数据库,通过对比两部分的操作,大致得到Mybatis所做的主要工作,为接下来的源码解析做准备 示例代码 完整的工 ...

  7. MyBatis3源码解析(8)MyBatis与Spring的结合

    简介 在上几篇文章中,解析了MyBatis的核心原理部分,我们大致对其有了一定的了解,接下来我们看看在日常的开发中MyBatis是如何与Spring框架结合的 源码解析 在我们的日常开发中,使用Spr ...

  8. MyBatis3源码解析(4)参数解析

    简介 上篇文章中探索了查询语句的执行过程,下面我们接着来看看其中的查询参数的解析细节,是如何工作的 参数的解析 在日常的开发中,常见的参数有如下几种: 1.直接传入: func(Object para ...

  9. mybatis源码解析(五) --- typehandler注册和处理的查询结果对象的类型转换

    上一次分析了mapper接口动态代理调用的处理过程,在查询结果后,然后转换的java所对应的类型,这篇文章将对这个类型转换处理详细介绍,在Configuration中TypeHandlerRegist ...

最新文章

  1. [纪录]仿IOS滚轮效果(竖直滑动选择器)
  2. 面试----Object类
  3. Storm中并行度原来是这样计算的(1.0.1版本)
  4. Ubuntu 设置程序开机启动(以指定用户身份)
  5. Mozilla “Common Voice” 开源语音识别项目
  6. [Cocos2d-x For WP8]Scene场景
  7. CF773F Test Data Generation(倍增FFT/动态规划)
  8. qt获取当前场景中的所有图形项的层次
  9. Java 多态的实现机制
  10. java中多态案例工厂类,Java中构造器内部的多态方法的行为实例分析
  11. java date nov_Java DateTime格式为此
  12. NC158 有向无环图的单源最短路径问题:Dijkstra算法
  13. 删除任务管理器中的启动项
  14. 98 服务器系统,流金岁月:重温Windows 98视窗操作系统
  15. wincc7.5系统语言切换功能(C脚本)
  16. 文献阅读1:Deep Learning for Image Super-resolution: A Survey
  17. 网页或大屏展示的倒计时器
  18. 基于QT的网络嗅探器实现(网络安全课程设计)
  19. windows远程连接服务器并映射端口访问目标服务
  20. 【vs2019】调试介绍

热门文章

  1. Flash Builder 使用
  2. mysql中怎样查看和删除唯一索引
  3. 動態設定GridView的列寬
  4. ThinkPHP3.2.3分页中文参数乱码问题及解决
  5. 推荐系统实战第二部分 评价指标
  6. 5-1 File Transfer
  7. 冠军奖金50万,2020腾讯广告算法大赛广发“英雄帖”
  8. 腾讯广告算法大赛 | 复赛第二周最佳进步奖得主心得分享
  9. 腾讯广告算法大赛 | 专家开小灶,独家解析Lookalike那些事儿
  10. jdbc数据库配置mysql数据库_JDBC连接MySQL数据库(一)——数据库的基本连接