本篇次要介绍一下 mybatis 的类型处理器,基于3.4.6版本。

知识点

  • 什么是类型处理器
  • 类型处理器的作用
  • 如何自定义类型处理器
  • 实现原理

什么是类型处理器

咱们平时在应用 mybatis 的时候比较简单,只有写一个 sql 语句,而后定义好对应的接口就能够应用了,这里就波及到一个问题:咱们调用接口的时候传入的明明是 java 的数据类型,为什么可能失常存到数据库里呢,咱们从数据库里取出来的明明是数据库中的类型,为什么可能间接在代码里拿到 java 类型呢?类型处理器就是来解决这个问题的,用 mybatis 的术语,就是 TypeHandler。

类型处理器的作用

其实下面也说到了,类型处理器的作用非常简单,总结起来就2种

  • 将咱们传入接口的参数转换为对应的数据库类型
  • 将数据库种查问回来的字段类型转换为对应的java类型

如何自定义类型处理器

咱们晓得 mybatis 内置了很多类型的处理器,这也是咱们什么都不做,间接可能实现类型转换的起因,看下目前有哪些类型处理器

能够看到,type目录下简直全是,有趣味的能够本人去看下,这里不多做介绍。如果遇到了内置处理器无奈解决的新类型,或者咱们想要在内置的类型处理器上加一些本人的逻辑,怎么办呢?这时就须要自定义类型处理器了。这个比较简单,其实官网文档里也介绍过了,这里再具体介绍一下
1) 继承 BaseTypeHandler ,我这里解决的是String 类型

public class MyTypeHandler extends BaseTypeHandler<String> {    @Override    public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {        preparedStatement.setString(i, s);    }    @Override    public String getNullableResult(ResultSet resultSet, String s) throws SQLException {        return null;    }    @Override    public String getNullableResult(ResultSet resultSet, int i) throws SQLException {        return null;    }    @Override    public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {        return null;    }}

2)最简略的状况下下面定义曾经能够了,咱们还能够本人指定处理器解决的数据库字段类型,通过@MappedJdbcTypes注解来指定,比方我这里要解决的是表中的 varchar 类型数据,如果不指定则默认取 key 为 null 的对应jdbc类型处理器

@MappedJdbcTypes(value = JdbcType.VARCHAR)

3) 另外还能够通过@MappedTypes注解来指定解决对应的 java 类型,如果没指定,则默认取 BaseTypeHandler 中对应的泛型类的理论类型

@MappedTypes(value = String.class)

4) 最初须要在配置中指定类型处理器的门路

mybatis.type-handlers-package=com.example.mybatisanalyze.typehandler

实现原理

注册流程

自定义一个类型处理器如此简略,它的实现原理也不难,咱们来看下是如何实现的。这里先看一个十分重要的类TypeHandlerRegistry,该类就是类型处理器治理类,所有的类型处理器都在这外面保护着

能够看到在构造函数里就会注册内置的类型处理器。在咱们配置了对应的类型处理器所在包门路之后,mybatis-spring 或者 mybatis就会调用对应的注册接口进行注册

这外面的逻辑比较简单,就不细说,晓得一点就能够:外面保护的 map 构造是 <javatype, map<jdbctype, typehandler>>,也就是先依据 java 类型取到对应的汇合,再通过表字段类型取到对应的处理器。

解决流程

对于解决流程,要调试的话,要害看一个类:BaseTypeHandler。这里用到了模板模式,所有的处理器都要先通过基类的模板解决,再调子类的定制化逻辑。它继承的是TypeHandler

从对应的接口名称中咱们也能看进去,setParameter 是用来将 java 类型转换为表字段类型的,其余的是用来讲表字段类型转换为 java 类型的。咱们平时在执行逻辑的时候用的是DefaultSqlSession,以selectOne函数来举例看下是在哪里做类型转换解决的。从org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)始终跟进去

这里默认用的缓存执行器,也就是CachingExecutor。持续跟进去

缓存不存在的话,会应用 BaseExecutor 来执行查问,两头的链路不多介绍了,间接看org.apache.ibatis.executor.SimpleExecutor#prepareStatement

在这里做参数的类型解决,跟进到org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters

能够看到这里就是遍历参数,依据参数类型获取对应的类型处理器一一解决。
再来看下对于数据库查回的后果集是如何做解决的,看下如下逻辑
org.apache.ibatis.executor.statement.PreparedStatementHandler#query

这里会调用对应的连接池执行数据库操作,而后进行后果解决。而后间接跟到org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap)

这里开始获取对应的行数据,我画红框的中央就是表字段和 Java 类型字段映射解决逻辑,跟进去看下

这里又呈现了 TypeHandler,这里就是具体的类型处理器对后果进行解决的逻辑。

总结

本文具体介绍了 mybatis 的类型处理器,看完你会发现原来类型处理器实现也是很简略的。