typeHandlers又叫类型处理器,就像在JDBC中,我们在PreparedStatement中设置预编译sql所需的参数或执行sql后根据结果集ResultSet对象获取得到的数据时,需要将数据库中的类型和java中字段的类型进行转换一样,在MyBatis中使用typeHandler来实现。所以说白了,typeHandlers就是用来完成javaType和jdbcType之间的转换。举个比较简单的例子,我创建一个博客表,表中的创建时间和修改时间用VARCHAR类型,但是在我的POJO对象中,创建时间和修改时间的类型是Date,这样我在向数据库插入数据时,需要将日期类型转化成VARCHAR,而从数据库中查询出的结果中,又需要将VARCHAR类型转换成Date.在MyBatis中,使用typeHandlers配置来实现这个转换过程。和别名一样,MyBatis中的类型处理器也存在系统定义的和自定义两种,MyBatis会根据javaType和jdbcType来决定采用哪个typeHandler来处理这些转换规则,而且系统定义的能满足大部分需求,可以说是非常好用,用户只需要自定义一些特有的转换规则,如枚举类型。下面分别介绍这两种typeHandler。

一、系统定义的typeHandler

同上一节的typeAliases,typeHandler也能通过Configuration对象获取到,如下:

  1. /**
  2. * 获取类型处理器
  3. */
  4. public static void getTypeHandlers() {
  5. SqlSession sqlSession = getSqlSession();
  6. TypeHandlerRegistry typeHandlerRegistry = sqlSession.getConfiguration().getTypeHandlerRegistry();
  7. Collection<TypeHandler<?>> handlers = typeHandlerRegistry.getTypeHandlers();
  8. System.out.println(handlers.size());
  9. for (TypeHandler<?> typeHandler : handlers) {
  10. System.out.println(typeHandler.getClass().getName());
  11. }
  12. }

执行结果如下:

  1. 39
  2. org.apache.ibatis.type.SqlDateTypeHandler
  3. org.apache.ibatis.type.ClobReaderTypeHandler
  4. org.apache.ibatis.type.LocalTimeTypeHandler
  5. org.apache.ibatis.type.YearMonthTypeHandler
  6. org.apache.ibatis.type.NStringTypeHandler
  7. org.apache.ibatis.type.LocalDateTypeHandler
  8. org.apache.ibatis.type.BigIntegerTypeHandler
  9. org.apache.ibatis.type.OffsetDateTimeTypeHandler
  10. org.apache.ibatis.type.ByteObjectArrayTypeHandler
  11. org.apache.ibatis.type.ArrayTypeHandler
  12. org.apache.ibatis.type.BigDecimalTypeHandler
  13. org.apache.ibatis.type.UnknownTypeHandler
  14. org.apache.ibatis.type.OffsetTimeTypeHandler
  15. org.apache.ibatis.type.ByteArrayTypeHandler
  16. org.apache.ibatis.type.DateOnlyTypeHandler
  17. org.apache.ibatis.type.JapaneseDateTypeHandler
  18. org.apache.ibatis.type.TimeOnlyTypeHandler
  19. org.apache.ibatis.type.NClobTypeHandler
  20. org.apache.ibatis.type.BooleanTypeHandler
  21. org.apache.ibatis.type.BlobTypeHandler
  22. org.apache.ibatis.type.YearTypeHandler
  23. org.apache.ibatis.type.BlobByteObjectArrayTypeHandler
  24. org.apache.ibatis.type.MonthTypeHandler
  25. org.apache.ibatis.type.FloatTypeHandler
  26. org.apache.ibatis.type.DateTypeHandler
  27. org.apache.ibatis.type.ClobTypeHandler
  28. org.apache.ibatis.type.BlobInputStreamTypeHandler
  29. org.apache.ibatis.type.ByteTypeHandler
  30. org.apache.ibatis.type.SqlTimestampTypeHandler
  31. org.apache.ibatis.type.ZonedDateTimeTypeHandler
  32. org.apache.ibatis.type.IntegerTypeHandler
  33. org.apache.ibatis.type.LocalDateTimeTypeHandler
  34. org.apache.ibatis.type.CharacterTypeHandler
  35. org.apache.ibatis.type.SqlTimeTypeHandler org.apache.ibatis.type.DoubleTypeHandler
  36. org.apache.ibatis.type.ShortTypeHandler
  37. org.apache.ibatis.type.LongTypeHandler
  38. 39 org.apache.ibatis.type.InstantTypeHandler
  39. 40 org.apache.ibatis.type.StringTypeHandler

从结果来看,系统总过定义了39个类型处理器。

现在选择其中的StringTypeHandler来进行分析,看看其源代码

1、获取StringTypeHandler的源代码

  1. /**
  2. * Copyright 2009-2015 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.ibatis.type;
  17.  
  18. import java.sql.CallableStatement;
  19. import java.sql.PreparedStatement;
  20. import java.sql.ResultSet;
  21. import java.sql.SQLException;
  22.  
  23. /**
  24. * @author Clinton Begin
  25. */
  26. public class StringTypeHandler extends BaseTypeHandler<String> {
  27.  
  28. @Override
  29. public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
  30. throws SQLException {
  31. ps.setString(i, parameter);
  32. }
  33.  
  34. @Override
  35. public String getNullableResult(ResultSet rs, String columnName)
  36. throws SQLException {
  37. return rs.getString(columnName);
  38. }
  39.  
  40. @Override
  41. public String getNullableResult(ResultSet rs, int columnIndex)
  42. throws SQLException {
  43. return rs.getString(columnIndex);
  44. }
  45.  
  46. @Override
  47. public String getNullableResult(CallableStatement cs, int columnIndex)
  48. throws SQLException {
  49. return cs.getString(columnIndex);
  50. }
  51. }

从上述代码可以看出它继承了一个叫做BaseTypeHandler<String>的类,这个类的范型是String,即javaType,几个方法如下:

1⃣️setNonNullParameter:这个方法是用来将javaType转换成jdbcTpe

2⃣️getNullableResult:这个方法用来将从结果集根据列名称获取到的数据的jdbcType转换成javaType

3⃣️getNullableResult:这个方法用来将从结果集根据列索引获取到的数据的jdbcType转换成javaType

4⃣️getNullableResult:这个方法用在存储过程中

其实主要就是完成不同类型之间的转换,下面来看一下它所继承的BaseTypeHandler<String>类

2、BaseTypeHandler<String>类源码

  1. /**
  2. * Copyright 2009-2015 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.ibatis.type;
  17.  
  18. import java.sql.CallableStatement;
  19. import java.sql.PreparedStatement;
  20. import java.sql.ResultSet;
  21. import java.sql.SQLException;
  22.  
  23. import org.apache.ibatis.executor.result.ResultMapException;
  24. import org.apache.ibatis.session.Configuration;
  25.  
  26. /**
  27. * @author Clinton Begin
  28. * @author Simone Tripodi
  29. */
  30. public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
  31.  
  32. protected Configuration configuration;
  33.  
  34. public void setConfiguration(Configuration c) {
  35. this.configuration = c;
  36. }
  37.  
  38. @Override
  39. public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
  40. if (parameter == null) {
  41. if (jdbcType == null) {
  42. throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
  43. }
  44. try {
  45. ps.setNull(i, jdbcType.TYPE_CODE);
  46. } catch (SQLException e) {
  47. throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
  48. "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
  49. "Cause: " + e, e);
  50. }
  51. } else {
  52. try {
  53. setNonNullParameter(ps, i, parameter, jdbcType);
  54. } catch (Exception e) {
  55. throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
  56. "Try setting a different JdbcType for this parameter or a different configuration property. " +
  57. "Cause: " + e, e);
  58. }
  59. }
  60. }
  61.  
  62. @Override
  63. public T getResult(ResultSet rs, String columnName) throws SQLException {
  64. T result;
  65. try {
  66. result = getNullableResult(rs, columnName);
  67. } catch (Exception e) {
  68. throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
  69. }
  70. if (rs.wasNull()) {
  71. return null;
  72. } else {
  73. return result;
  74. }
  75. }
  76.  
  77. @Override
  78. public T getResult(ResultSet rs, int columnIndex) throws SQLException {
  79. T result;
  80. try {
  81. result = getNullableResult(rs, columnIndex);
  82. } catch (Exception e) {
  83. throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set. Cause: " + e, e);
  84. }
  85. if (rs.wasNull()) {
  86. return null;
  87. } else {
  88. return result;
  89. }
  90. }
  91.  
  92. @Override
  93. public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
  94. T result;
  95. try {
  96. result = getNullableResult(cs, columnIndex);
  97. } catch (Exception e) {
  98. throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement. Cause: " + e, e);
  99. }
  100. if (cs.wasNull()) {
  101. return null;
  102. } else {
  103. return result;
  104. }
  105. }
  106.  
  107. public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  108.  
  109. public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
  110.  
  111. public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
  112.  
  113. public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
  114.  
  115. }

此类是一个抽象类,重写了四个接口之外,还有四个抽象方法,也就是在StringTypeHandler中实现的接口,那么重写的四个接口是什么呢?我们去看一下它所实现的TypeHandler<T>接口

3、接口TypeHandler<T>源代码

  1. /**
  2. * Copyright 2009-2015 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.ibatis.type;
  17.  
  18. import java.sql.CallableStatement;
  19. import java.sql.PreparedStatement;
  20. import java.sql.ResultSet;
  21. import java.sql.SQLException;
  22.  
  23. /**
  24. * @author Clinton Begin
  25. */
  26. public interface TypeHandler<T> {
  27.  
  28. void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  29.  
  30. T getResult(ResultSet rs, String columnName) throws SQLException;
  31.  
  32. T getResult(ResultSet rs, int columnIndex) throws SQLException;
  33.  
  34. T getResult(CallableStatement cs, int columnIndex) throws SQLException;
  35.  
  36. }

这里也定义了四个接口,一个set方法用来将javaType转为jdbcType,三个get方法用来将jdbcType转为javaType,而在BaseTypeHandler中实现的就是这四个方法。

所以当我们自定义自己的typeHandler时有两种方法:

第一种:继承BaseTypeHandler类

第二种:实现TypeHandler接口

二、自定义typeHandler

我使用实现TypeHandler接口的方式创建一个MyDateHandler,用来完成javaType中的Date类型与jdbcType中的varchar类型之间的转化。

创建MyDateHandler

  1. package com.daily.handler;
  2.  
  3. import java.sql.CallableStatement;
  4. import java.sql.PreparedStatement;
  5. import java.sql.ResultSet;
  6. import java.sql.SQLException;
  7. import java.text.SimpleDateFormat;
  8. import java.util.Date;
  9.  
  10. import org.apache.ibatis.type.DateTypeHandler;
  11. import org.apache.ibatis.type.JdbcType;
  12. import org.apache.ibatis.type.TypeHandler;
  13. import org.postgresql.jdbc2.optional.SimpleDataSource;
  14.  
  15. /**
  16. * 自定义一个TypeHandler用来将javatype的日期类型和jdbctype的VARCHAR进行转换
  17. */
  18. public class MyDateHandler implements TypeHandler<Date> {
  19. @Override
  20. // 设置sql中指定索引的参数,即将javaType转化为jdbcType
  21. public void setParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
  22. //设置数据存储到数据库中的格式
  23. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  24. ps.setString(i, sdf.format(parameter));
  25. }
  26.  
  27. @Override
  28. // 根据列名称从结果集获取值,并将jdbcType转换成javaType
  29. public Date getResult(ResultSet rs, String columnName) throws SQLException {
  30. String columnValue = rs.getString(columnName);
  31. if (null != columnValue) {
  32. return new Date(Long.valueOf(columnValue));
  33. }
  34. return null;
  35. }
  36.  
  37. @Override
  38. // 根据列名称从结果集获取值,并将jdbcType转换成javaType
  39. public Date getResult(ResultSet rs, int columnIndex) throws SQLException {
  40. String columnValue = rs.getString(columnIndex);
  41. if (null != columnValue) {
  42. return new Date(Long.valueOf(columnValue));
  43. }
  44. return null;
  45. }
  46.  
  47. @Override
  48. public Date getResult(CallableStatement cs, int columnIndex) throws SQLException {
  49. String columnValue = cs.getString(columnIndex);
  50. if (null != columnValue) {
  51. return new Date(Long.valueOf(columnValue));
  52. }
  53. return null;
  54. }
  55.  
  56. }

下面用一个例子来说明这个handler的使用。

1⃣️创建一个blog表,表中的时间用varchar类型

2⃣️创建POJO对象,将create_time和modify_time的类型定义为Date

  1. package com.daily.pojo;
  2.  
  3. import java.util.Date;
  4.  
  5. public class Blog {
  6. private Integer blogId;
  7.  
  8. private String blogTitle;
  9.  
  10. private String blogContent;
  11.  
  12. private Date createTime;
  13.  
  14. private Date modifyTime;
  15.  
  16. public Integer getBlogId() {
  17. return blogId;
  18. }
  19.  
  20. public void setBlogId(Integer blogId) {
  21. this.blogId = blogId;
  22. }
  23.  
  24. public String getBlogTitle() {
  25. return blogTitle;
  26. }
  27.  
  28. public void setBlogTitle(String blogTitle) {
  29. this.blogTitle = blogTitle == null ? null : blogTitle.trim();
  30. }
  31.  
  32. public String getBlogContent() {
  33. return blogContent;
  34. }
  35.  
  36. public void setBlogContent(String blogContent) {
  37. this.blogContent = blogContent == null ? null : blogContent.trim();
  38. }
  39.  
  40. public Date getCreateTime() {
  41. return createTime;
  42. }
  43.  
  44. public void setCreateTime(Date createTime) {
  45. this.createTime = createTime == null ? null : createTime;
  46. }
  47.  
  48. public Date getModifyTime() {
  49. return modifyTime;
  50. }
  51.  
  52. public void setModifyTime(Date modifyTime) {
  53. this.modifyTime = modifyTime == null ? null : modifyTime;
  54. }
  55. }

3⃣️在mybatis-config.xml文件中注册刚才自定义的MyDateHandler

  1. <!--类型处理器 -->
  2. <typeHandlers>
  3. <!-- 注册自定义handler,说明它作用的jdbcType和javaType -->
  4. <typeHandler jdbcType="VARCHAR" javaType="date" handler="com.daily.handler.MyDateHandler" />
  5. </typeHandlers>

4⃣️在BlogMapper.xml文件中使用

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.daily.dao.BlogMapper">
  4. <resultMap id="BaseResultMap" type="com.daily.pojo.Blog">
  5. <id column="blog_id" jdbcType="INTEGER" property="blogId" />
  6. <result column="blog_title" jdbcType="VARCHAR" property="blogTitle" />
  7. <result column="blog_content" jdbcType="VARCHAR" property="blogContent" />
  8. <!-- 不使用MyDateHandler-->
  9. <result column="create_time" jdbcType="VARCHAR" property="createTime" />
  10. <!-- 使用MyDateHandler-->
  11. <result column="modify_time" typeHandler="com.daily.handler.MyDateHandler" property="modifyTime" />
  12. </resultMap>
  13.  
  14. <insert id="insert" parameterType="com.daily.pojo.Blog">
  15. insert into blog (blog_id, blog_title, blog_content,
  16. create_time, modify_time)
  17. values (#{blogId,jdbcType=INTEGER}, #{blogTitle,jdbcType=VARCHAR},
  18. #{blogContent,jdbcType=VARCHAR},
  19. #{createTime,jdbcType=VARCHAR}, #{modifyTime,typeHandler=com.daily.handler.MyDateHandler})
  20. </insert>
  21. </mapper>

5⃣️查看结果

第一种:create_time不设置typeHandler,modify_time设置typeHandler

从上面的结果来看,虽然create_time没有设置typeHandler,但是结果跟使用了typeHandler的modify_time是一样的,那么我把两个都去掉呢?

第二种:两个都不设置

可以看到结果也是一样,下面我去掉注册的MyDateHandler呢?

第三种:去掉MyDateHandler

从上述结果可以看到,当去掉自定义的处理器时,MyBatis会根据结果自动选择合适的handler进行转换。

日常开发中,一般不需要定义,使用默认的就可以,除非是像枚举这种特殊类型就需要自己实现。

以上就是MyBatis配置文件中typeHandlers的配置内容。

MyBatis配置文件(四)--typeHandlers的更多相关文章

  1. MyBatis学习(四)、MyBatis配置文件

    四.MyBatis主配置文件 在定义sqlSessionFactory时需要指定MyBatis主配置文件: <bean id="sqlSessionFactory" clas ...

  2. MyBatis学习 之 四、MyBatis配置文件

    目录(?)[-] 四MyBatis主配置文件 properties属性 settings设置 typeAliases类型别名 typeHandlers类型句柄 ObjectFactory对象工厂 pl ...

  3. Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解

    封面:洛小汐 作者:潘潘 做大事和做小事的难度是一样的.两者都会消耗你的时间和精力,所以如果决心做事,就要做大事,要确保你的梦想值得追求,未来的收获可以配得上你的努力. 前言 上一篇文章 <My ...

  4. 详解mybatis配置文件

    在前面两篇文章中,大致与大家分享了基于Java的ORM框架,Mybatis基本架构和Mybatis实现CRUD的基本原理与流程,在本篇文章中,继续与大家分享Mybatis配置文件. 通过研究Mybat ...

  5. Mybatis配置文件

    XML 映射配置文件 MyBatis 的配置文件包含了设置(settings)和属性(properties)信息. properties 这些属性都是可外部配置且可动态替换的,既可以在典型的 Java ...

  6. MyBatis配置文件解析

    MyBatis配置文件解析(概要) 1.configuration:根元素 1.1 properties:定义配置外在化 1.2 settings:一些全局性的配置 1.3 typeAliases:为 ...

  7. 02.MyBatis配置文件详解

        MyBatis入参考文档:http://mybatis.org/mybatis-3/zh/  1.properties 属性 1.在MyBatis配置文件中引用属性文件     MyBatis ...

  8. Mybatis(一):MyBatis配置文件config.xml详解

    MyBatis 配置文件基本结构 在使用mybatis框架时,首先导入其对应的jar包,并进行相应的配置,所以得对配置文件的每个参数都得了解.一个完全的mybatis配置文件结构如下: <?xm ...

  9. mybatis 学习四 源码分析 mybatis如何执行的一条sql

    总体三部分,创建sessionfactory,创建session,执行sql获取结果 1,创建sessionfactory      这里其实主要做的事情就是将xml的所有配置信息转换成一个Confi ...

随机推荐

  1. 在任务管理中显示进程ID号

  2. 次短路 /// dijkstra oj1597

    题目大意: 给出一个有向图,求从 顶点a 到 顶点b 的次短路. 第一行是2个正整数 n 和 e,表示该有向图的顶点数和边数.3 < n ≤ 5000 , 3 < e < 40000 ...

  3. Activiti学习笔记2 — HelloWorld

    一. Maven的POM配置文件 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="htt ...

  4. kali linux 入门(1) 基于win10和docker的环境搭建

    1. 前言 渗透测试并没有一个标准的定义.国外一些安全组织达成共识的通用说法是,渗透测试是通过模拟恶意黑客的攻击方法,来评估计算机网络系统安全的一种评估方法,这个过程包括对系统的任何弱点.技术缺陷或漏 ...

  5. Nginx的安装--------tar包安装

    Nginx的安装,在网上搜索是很多的结果,但是 真的安装起来却花费了不少 心思,总结起来就是依赖包安装了,但是没有指定对的路径,在安装的过程中遇到过两个问题: ①make[1]: *** [/usr/ ...

  6. 实现Linux下不间断聊天和退出处理

    实现Linux下不间断聊天和退出处理

  7. CF1163E Magical Permutation

    题意:给定集合,求一个最大的x,使得存在一个0 ~ 2x - 1的排列,满足每相邻的两个数的异或值都在S中出现过.Si <= 2e5 解:若有a,b,c,令S1 = a ^ b, S2 = b ...

  8. csps模拟68d,e,f题解

    题面:https://www.cnblogs.com/Juve/articles/11655531.html 三道数据结构? d: 贪心,先按a排序,然后枚举删了前i个a值比较小的,然后在剩下的里面删 ...

  9. ng-zorro-mobile中遇到的问题

    一.Modal(弹出框)使用上的问题 在官方文档中,Modal是这样使用的: 这里需要注意的一点就是,看到上方代码中只用了Modal的全局方式,所以个人认为下面这段注入初始化的东西是没有用的便去掉: ...

  10. vue 自定义指令input表单的数据验证

    一.代码 <template> <div class="check" > <h3>{{msg}}</h3> <div clas ...