在日常开发中使用mybatis时,mybatis的mapper.xml、mapper接口、entity实体一般会由mybatis-generator自动生成,其中实体的每个属性与数据库表的列一一对应,这些类型往往都是基本类型的包装类或时间类型Date;

你可能会思考一个问题:当我们在自定义了某个枚举类型来对应数据库中的某个列(如int、tinyint类型)时,能否让mybatis知道如何做枚举与数据库类型的映射关系,从而不需要我们在代码中手动的做二者属性值的转换?——实际上是可以的,尽管我们往往建议:

业务层的各个实体与数据库的实体做好隔离,在代码中显示的完成二者之间的属性值转换;

但是MyBatis还是为我们提供了入口——TypeHandler类型处理器;

1. TypeHandler是什么?能做什么?

TypeHandler,类型转换器,在mybatis中是一个比较重要的功能,用于实现java类型和JDBC类型的相互转换;

mybatis使用prepareStatement来进行参数设置的时候,需要通过typeHandler将传入的java参数设置成合适的jdbc类型参数,这个过程实际上是通过调用PrepareStatement不同的set方法实现的;

在获取结果返回之后,也需要将返回的结果转换成我们需要的java类型,这时候是通过调用ResultSet对象不同类型的get方法时间的;所以不同类型的typeHandler其实就是调用PrepareStatement和ResultSet的不同方法来进行类型的转换,有些时候会在调用PrepareStatement和ResultSet的相关方法之前,可以对传入的参数进行一定的处理;

当我们没有指定typeHandler的时候mybatis会根据传入参数的类型和返回值的类型调用默认的typeHandler进行处理;对于一个typeHandler需要配置java类型(javaType)和JDBC类型(jdbcType),typeHandler的作用就是实现这两种类型的转换,在传入的参数为指定的Java类型时,将其转换为指定的JDBC类型,当返回值为指定JDBC类型时将其转换为配置的Java类型;

2. TypeHandler如何在mybatis的执行过程中起作用?

关于Mybatis的执行流程,参考《Mybatis——执行流程及关键代码走读》这篇文章,里面有讲到PrepareStatement会执行parameterize从而完成SQL参数的填充,具体看下面的源码:

(1)先从SimpleExecutor的prepareStatement()方法开始

(2)然后是StatementHandler的parameterize()方法,PreparedStatementHandler是StatementHandler的实现类,实际上是ParameterHandler#setParameters方法;

(3)ParameterHandler#setParameters方法,DefaultParameterHandler是ParameterHandler的实现类,实际是调用DefaultParameterHandler#setParameters方法;

接下来关注几个重要的对象/接口/类:

typeHandlerRegistry对象

mybatis默认定义了一批TypeHandler,正常情况下这些TypeHandler就可以满足我们的使用了;mybatis通过TypeHandlerRegister来管理TypeHandler ,这个类里面可以看到所有定义好的typeHandler;

可以看到对于一个Java类型是可以有多个JDBC类型相对应的,所以会存在多个TypeHandler,在这种情况下需要根据传入参数的javaType以及其在数据库中对应JdbcType一起来选定一个TypeHandler进行处理;

下面来看一个预定义好的TypeHandler的实现——TimeOnlyTypeHandler,实现Java中Date类型和jdbc中JdbcType;Time类型转换的TimeOnlyTypeHandler;JDBC中的Time类型只记录时分秒,所以如果我们传入一个代表2017-11-21 11:55:59的Date对象,那么数据库中存储的时间是11:55:59;下面看下具体的源码实现:

可以看到,TimeOnlyTypeHandler实现了BaseTypeHandler;

BaseTypeHandler是TypeHandler接口的实现类;

TypeHandler接口

其中 setParameter 方法用于把 java 对象设置到 PreparedStatement 的参数中,getResult 方法用于从 ResultSet(根据列名或者索引位置获取) 或 CallableStatement(根据存储过程获取) 中取出数据转换为 java 对象。

实际开发中,我们可以继承 org.apache.ibatis.type.BaseTypeHandler 类型来实现自定义类型处理器。这个类型是抽象类型,实现了 TypeHandler 的方法进行通用流程的封装,做了异常处理,并定义了几个类似的抽象方法,如下所示。继承 BaseTypeHandler 类型可以极大地降低开发难度;

3. TypeHandler使用示例

使用自定义一般需要3步;

  • 实现BaseTypeHandler的抽象方法,指定JDBC类型和Java类型;
  • 将自定义TypeHandler注册到mybatis中(TypeHandlerRegister);
  • 在mapper.xml中显示的使用自定义TypeHandler,包括执行前的参数类型转换和执行后的结果参数类型转换;

虽然大部分时候mybatis提供的typeHandler已经够用了,但总有些情况下需要我们自己定义TypeHandler;下面给一个应用场景的代码示例来说明如何自己定义和使用TypeHandler;

现在的场景是:首先是建立了一个person表存储个人信息,这个表里有一栏hobbys是记录个人爱好的,爱好包括足球,排球,游泳之类的,在Java的Person对象中对应的是一个String类型的list,而在数据库中是以逗号分隔的字符串表示的,心事如”足球,排球,游泳”,所以现在需要定义一个TypeHandler实现在插入数据和查询数据时string和list类型的相互转换;

自定义一个TypeHandler需要继承TypeHandler T接口,T是传入的java类型,并需要使用@MappedTypes定义需要被拦截的java类型@MappedJdbcTypes配置jdbc类型,这里的JdbcType必须org;apache;ibatis;type;JdbcType中的枚举类型,然后还需要实现TypeHandler接口中一系列的get/set方法,具体实现的代码如下:

@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes({List.class})
public class ListTypeHandler implements TypeHandler<List<String>> {@Overridepublic void setParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {String hobbys = StringUtils.join(parameter, ",");try {ps.setString(i, hobbys);} catch (Exception e) {e.printStackTrace();}}@Overridepublic List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {String hobbys = cs.getString(columnIndex);return Arrays.asList(hobbys.split(","));}@Overridepublic List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {return Arrays.asList(rs.getString(columnIndex).split(","));}@Overridepublic List<String> getResult(ResultSet rs, String columnName) throws SQLException {return Arrays.asList(rs.getString(columnName).split(","));}
}

自定义完成之后就需要在mybatis-config.xml文件中配置以注册到mybatis中;

<typeHandlers><typeHandler jdbcType="VARCHAR" javaType="list" handler="com.akira.dal.typeHandler.ListTypeHandler"/>
</typeHandlers>

注册完成之后也仍然不能起作用,因为还需要标识那些参数和返回的结果是需要使用这个TypeHandler进行处理的;具体来说,在插入数据和对返回结果进行处理的时候,可以对参数配置javaType和jdbcType或直接配置typeHandler属性来进行标识;下面是插入数据时标识用指定TypeHandler进行处理;

<insert id="insertPerson" parameterType="person">INSERT INTO person (id,name,sex,hobbys,data_time) values(#{id},#{name},#{sex},#{hobbys,typeHandler=com.akira.dal.typeHandler.ListTypeHandler},#{date})
</insert>

下面是标识对返回的结果用指定TypeHandler进行处理;

<resultMap id="personMap" type="person"><id property="id" column="id"/><result property="name" column="name"/><result property="sex" column="sex"/><result property="hobbys" column="hobbys" typeHandler="com.akira.dal.typeHandler.ListTypeHandler"/><result property="date" column="data_time"/></resultMap>

总结下Mybatis的类型处理器的使用过程:

(1)MyBatis启动后首先把typeHandler注册进去;首先尝试读取MappedTypes注解,如果有这个注解定义了java类型,则把这个类型处理器注册到相应的java类型的处理器中;如果没有使用注解,但是继承了TypeReference类型,比如前面提到的BaseTypeHandler,则通过TypeReference的接口获取原始类型注册到相应的java类型的处理器中。如果实在是获取不到java类型,则按照无类型处理;

(2)MyBatis在预处理语句设置参数时调用TypeHandler进行java对象到jdbc的PreparedStatement参数值的转换;

(3)MyBatis查询数据库完成后,调用TypeHandler的方法读取数据转换成java对象;

参考:

Mybatis之TypeHandler使用教程

MyBatis自定义类型处理器

Mybatis——类型处理器TypeHandler相关推荐

  1. MyBatis(九):MyBatis类型处理器(TypeHandler)详解

    TypeHandler简介 TypeHandler,顾名思义类型转换器,就是将数据库中的类型与Java中的类型进行相互转换的处理器. MyBatis 在设置预处理语句(PreparedStatemen ...

  2. MyBatis自定义类型处理器 TypeHandler

    在项目开发中经常会遇到一个问题: 当我们在javabean中自定义了枚举类型或者其它某个类型,但是在数据库中存储时往往需要转换成数据库对应的类型,并且在从数据库中取出来时也需要将数据库类型转换为jav ...

  3. 【Mybatis】类型处理器TypeHandler的作用与自定义

    一.什么是类型处理器 1.类型处理器(TypeHandler) MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时,都会用类型处理器将获取到的值以合 ...

  4. desc 枚举类型id_想让代码更优雅?Mybatis类型处理器了解一下!

    明确需求 在设计之初,sys_role表的enabled字段有2个可选值,其中0 代表禁用,1代表启用,而且实体类中我们使用的是Interger类型: 源码展示 /** * 有效标志 */ priva ...

  5. MyBatis-Plus——字段类型处理器TypeHandler

    字段类型处理器(TypeHandler) 1,准备工作 (1)MyBatis 中的 TypeHandler 类型处理器用于 JavaType 与 JdbcType 之间的转换,假设我们用户表中有一个联 ...

  6. MyBatis类型处理器注册器TypeHandlerReister

    一.引言 我们知道 mysql的类型和java类型有对应关系,参考此文:https://www.cnblogs.com/jerrylz/p/5814460.html. 那么mybatis是怎么找到这种 ...

  7. 类型处理器TypeHandler

    作用:用于JavaType和JdbcType之间的转换 解析:当Mybatis将一个Java对象写入数据库时,它会创建一个PreparedStatument对象,并且使用setValue()方法对占位 ...

  8. Ibatis的类型处理器TypeHandler解析

    Ibatis允许用户像在hibernate中一样定义自己的类型,但是,用户自定义类型需要与数据库中的字段类型进行对应.它的处理方法是允许我们扩展TypeHandler.Ibatis框架在处理该数据类型 ...

  9. MyBatis核心源码剖析(SqlSession XML解析 Mapper executor SQL执行过程 自定义类型处理器 缓存 日志)

    MyBatis核心源码剖析 MyBatis核心源码剖析 1 MyBatis源码概述 1.1 为什么要看MyBatis框架的源码 1.2 如何深入学习MyBatis源码 1.3 源码分析的5大原则 2 ...

最新文章

  1. Lua基本语法-书写规范以及自带常用函数
  2. 大数据环境下该如何优雅地设计数据分层
  3. 打开闪光灯java代码_android 拍照带水印(可打开闪光灯功能)
  4. Spring模板对象之RedisTemplate(Spring整合jedis)
  5. C#中谁最快:结构还是类?
  6. LRU缓存 数据结构设计(C++)
  7. 雾山五行专题高清壁纸,绝美动漫场景
  8. 做深度学习需要知道哪些linux指令?
  9. PL/SQL 调用JAVA使用UDP发送数据
  10. MYSQL建表操作大全
  11. 计算机办公模式是什么,华为Mate 10“电脑模式”告诉你什么是真正的“移动办公”...
  12. Liunx下Qos功能实现简析
  13. 新办林业规划资质(丙级)应具备的条件?
  14. TensorFlow Object Detection API Custom Object Hangs On
  15. 虚拟机 硬盘空间不足 磁盘最大大小调整的相对方法
  16. Qt图例类QLegend详解
  17. 帝国cms模板html文件夹,帝国CMS模板建站e文件夹各个文件功能说明
  18. 【RL-TCPnet网络教程】第4章 RL-TCPnet网络协议栈简介
  19. HTML中打开新页面的方法
  20. vue 微信公众号获取图片素材接口

热门文章

  1. IT职场生活工作感悟
  2. 惊呆了!我用 Python 可视化分析和预测了 2022 年 FIFA世界杯
  3. 微信公众号签到,签到后在活动大屏中实时展示签到人信息,也可以导出签到人信息用于抽奖
  4. 斜率、弧度、角度的转换
  5. 【JavaScript打印100,1000,10000 ......内的素数】自动打印素数
  6. php internetshortcut,shortcut功能
  7. hMailServer 配置
  8. 纽约州立大学环境与林业学院计算机科学专业,纽约州立大学环境科学与林业学院...
  9. 缺陷修改实践——replace函数的运用|思考?
  10. MFC添加加瓦系列一MFC编写的增量更新软件