栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

封面:洛小汐
作者:潘潘

做大事和做小事的难度是一样的。两者都会消耗你的时间和精力,所以如果决心做事,就要做大事,要确保你的梦想值得追求,未来的收获可以配得上你的努力。

前言

上一篇文章 《Mybatis系列全解(三):Mybatis简单CRUD使用介绍》 ,我们基本上手了 Mybatis 的增删改查操作,也感受到 Mybatis 的简单高效舒美,但是肯定有部分朋友对于 Mybatis 的配置文件只是了解基本组成和大致用法,尚无一套完整的结构记忆,所以本篇文章我们将详细的介绍 Mybatis 的配置全貌,毕竟 Mybatis 的配置文件对于整个 Mybatis 体系的构建与支撑有着深远的影响。

Mybatis系列全解脑图分享,持续更新中


Mybaits系列全解 (持续更新)
  • Mybatis系列全解(一):手写一套持久层框架
  • Mybatis系列全解(二):Mybatis简介与环境搭建
  • Mybatis系列全解(三):Mybatis简单CRUD使用介绍
  • Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解
  • Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件
  • Mybatis系列全解(六):Mybatis最硬核的API你知道几个?
  • Mybatis系列全解(七):全息视角看Dao层两种实现方式之传统方式与代理方式
  • Mybatis系列全解(八):Mybatis的动态SQL
  • Mybatis系列全解(九):Mybatis的复杂映射
  • Mybatis系列全解(十):Mybatis注解开发
  • Mybatis系列全解(十一):Mybatis缓存全解
  • Mybatis系列全解(十二):Mybatis插件开发
  • Mybatis系列全解(十三):Mybatis代码生成器
  • Mybatis系列全解(十四):Spring集成Mybatis
  • Mybatis系列全解(十五):SpringBoot集成Mybatis
  • Mybatis系列全解(十六):Mybatis源码剖析

目录

1、为什么要使用配置文件

2、Mybatis 配置全貌

3、XML 核心配置

4、XML 映射文件

5、总结

为什么要使用配置文件

试想,如果没有配置文件,我们的应用程序将只能沿着固定的姿态运行,几乎不能做任何动态的调整,那么这不是一套完美的设计,因为我们希望拥有更宽更灵活的操作空间和更多的兼容度,同时也能解决硬编码等问题,所以我们需要有配置文件,对应用程序进行参数预设和设置初始化工作。

那我们为何钟情XML

首先,当然是 XML 配置文件本身就足够优秀,格式规范,存储小,跨平台,读取快…等等,所谓窈窕淑女,谁人不爱。

其次,也是一个重要影响因素,就是各大领域大佬的支持,像微软、像Java系…等等,世上本无路,只是走的人多了,也就成了路 ( 这句话是鲁迅老先生说的)。

所以,Mybatis选择搭配XML配置,实属合理。

Mybatis 配置全貌

Mybatis框架本身,理论上就一个配置文件,其实也只需要一个配置文件,即mybatis-config.xml (当然文件名允许自由命名),只不过这个配置文件其中的一个属性mappers(映射器),由于可能产生过多的SQL映射文件,于是我们物理上单独拓展出来,允许使用者定义任意数量的 xxxMapper.xml 映射文件。

把SQL映射文件单独配置,是有好处的,一是灵活度上允许任意拓展,二也避免了其它无需经常变动的属性配置遭遇误改。

我们看看Mybatis官网给出的配置文件层次结构:

  • configuration(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
      • 三种别名定义方式
    • typeHandlers(类型处理器)
      • 自定义类型处理器
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
          • 三种支持数据源与自定义数据源
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

实际配置文件XML内容如下,除了约束头 与 ,

其余标签元素都是 Mybatis 的核心配置属性 :






    
    

    
    
 
    

    
    
 
    

    
    

    
    

    
    
 
    

    
    
 
 
     
     
     
     
  
  
  
  
     
 
    

    
    

    
    
 
    
    


必须注意:Mybatis配置文件的属性位置顺序是 固定 的,不允许 颠倒顺序,否则 Mybatis 在解析 XML 文件的时候就会抛出异常,这个与 Mybatis 框架启动加载配置信息顺序有关,后续我们源码分析会讲到。

以上基本能够清晰看明白 Mybatis 配置文件的层次结构关系,我们简单画一张脑图:

基本是需要我们掌握 9 大顶级元素配置,其中标记 橘红色 的属性配置,由于涉及 插件动态SQL ,插件配置可以应用于分页与功能增强等,动态SQL例如 if 标签、where 标签、foreach标签等,初步理解为应用于SQL语句拼接。这两块属于 Mybatis 的两个特性,我们后续单独详细进行梳理讨论。

XML 核心配置

我们的核心配置文件 configuration(配置)作为最顶级节点,其余 9 大属性都必须嵌套在其内,对于内部 9 大节点,我们逐一讲解:

1、properties(属性)

属性标签,显而易见就是提供属性配置,可进行动态替换,一般可以在 Java 属性文件中配置,例如 jdbc.properties 配置文件 ,或通过 properties 元素标签中的子元素 property 来指定配置。

举例我们需要配置数据源信息,采用 property 标签可以这样配置:



  
  
  
  


设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:



  
  
  
  


或者我们使用 Java 中的属性配置文件,把属性配置元素具体化到一个属性文件中,并且使用属性文件的 key 名作为占位符。例如 jdbc.properties


driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/myDB
username=root
password=123456

使用时我们把属性文件引入,并使用文件中定义的占位符,例如 db.driver :




    

  
  
  
  


但是问题来了,当我们既使用 *.properties 配置文件,同时又设置了 property 元素值,Mybatis 会使用哪边配置的属性值呢? 例如这种情况 :

 
  
  
  
  


这里,如果在 property 标签元素与 jdbc.properties 文件中同时存在相同属性,那么属性文件将会覆盖 property 标签元素的属性,例如最终 username属性值会使用 jdbc.properties 文件中设置的 root,而不会使用属性元素设置的 user1 。这样实际为配置提供了诸多灵活选择。

另外,properties 元素允许配置 resource 属性或 url 属性,只能二选一,要么使用 resource 指定本地的配置文件,要么使用 url 指定远程的配置文件,因为 Mybatis 在加载配置时,如果发现 url 与 resource 同时存在,会抛出异常禁止。




    




    


还有一种情况,像 Mybatis 在解析配置的时候,也可以在 Java 代码中构建属性 java.util.Properties 属性对象并传递到 SqlSessionFactoryBuilder.build() 方法中,例如:

// 构建属性对象
Properties props = new Properties();
props.setProperty("driver","com.mysql.jdbc.Driver"); 
props.setProperty("url","jdbc:mysql://127.0.0.1:3306/myDB"); 
props.setProperty("username","user1"); 
props.setProperty("password","123456");  

// 传递属性构建 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);

那么这三种方式都允许配置,那在属性配置重复的情况下,优先级别是怎样呢?

properties 优先级

1、第一优先级:在 Java 代码中构建的 properties 属性对象;

2、第二优先级:通过属性 resource 或 url 读取到的本地文件或远程文件;

3、第三优先级:直接在 properties 内部子标签元素 property 中设置的属性。

注意,在实际开发中,为了避免给后期维护造成困扰,建议使用单一种配置方式。

2、settings(设置)

settings 标签元素,是 MyBatis 中极为重要的调整设置,它们会动态改变 MyBatis 的运行时行为,这些配置就像 Mybatis 内置的许多功能,当你需要使用时可以根据需要灵活调整,并且 settings 能配置的东西特别多,我们先来一起看看,一个完整的属性配置示例:



  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  <... more .../>


  • 属性cacheEnabled

    • 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存
    • 支持 true | false
    • 默认 true
  • 属性lazyLoadingEnabled

    • 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
    • 支持 true | false
    • 默认 false
  • 属性 aggressiveLazyLoading

    • 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。
    • 支持 true | false
    • 默认 false (在 3.4.1 及之前的版本中默认为 true)
  • 属性 multipleResultSetsEnabled

    • 是否允许单个语句返回多结果集(需要数据库驱动支持)。
    • 支持 true | false
    • 默认 true
  • 属性 useColumnLabel

    • 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。
    • 支持 true | false
    • 默认 true
  • 属性 useGeneratedKeys

    • 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。
    • 支持 true | false
    • 默认 false
  • 属性 autoMappingBehavior

    • 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。
    • 支持 NONE, PARTIAL, FULL
    • 默认 PARTIAL
  • 属性 autoMappingUnknownColumnBehavior

    • 指定发现自动映射目标未知列(或未知属性类型)的行为。
      • NONE: 不做任何反应
      • WARNING: 输出警告日志( org.apache.ibatis.session.AutoMappingUnknownColumnBehavior 的日志等级必须设置为 WARN)
      • FAILING: 映射失败 (抛出 SqlSessionException)
    • 支持 NONE, WARNING, FAILING
    • 默认 NONE
  • 属性 defaultExecutorType

    • 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。
    • 支持 SIMPLE REUSE BATCH
    • 默认 SIMPLE
  • 属性 defaultStatementTimeout

    • 设置超时时间,它决定数据库驱动等待数据库响应的秒数。
    • 支持 任意正整数
    • 默认 未设置 (null)
  • 属性 defaultFetchSize

    • 动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。
    • 支持 任意正整数
    • 默认 未设置 (null)
  • 属性 defaultResultSetType

    • 指定语句默认的滚动策略。(新增于 3.5.2)
    • 支持 FORWARD_onLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)
    • 默认 未设置 (null)
  • 属性 safeRowBoundsEnabled

    • 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。
    • 支持 true | false
    • 默认 false
  • 属性 safeResultHandlerEnabled

    • 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。
    • 支持 true | false
    • 默认 true
  • 属性 mapUnderscoreToCamelCase

    • 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。
    • 支持 true | false
    • 默认 false
  • 属性 localCacheScope

    • MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。
    • 支持 SESSION | STATEMENT
    • 默认 SESSION
  • 属性 jdbcTypeForNull

    • 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
    • JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。
    • 默认 OTHER
  • 属性 lazyLoadTriggerMethods

    • 指定对象的哪些方法触发一次延迟加载。
    • 支持 用逗号分隔的方法列表。
    • 默认 equals,clone,hashCode,toString
  • 属性 defaultscriptingLanguage

    • 指定动态 SQL 生成使用的默认脚本语言。
    • 支持 一个类型别名或全限定类名。
    • 默认 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
  • 属性 defaultEnumTypeHandler

    • 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)
    • 支持 一个类型别名或全限定类名。
    • 默认 org.apache.ibatis.type.EnumTypeHandler
  • 属性 callSettersOnNulls

    • 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。
    • 支持 true | false
    • 默认 false
  • 属性 returnInstanceForEmptyRow

    • 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)
    • 支持 true | false
    • 默认 false
  • 属性 logPrefix

    • 指定 MyBatis 增加到日志名称的前缀。
    • 支持 任何字符串
    • 默认 未设置
  • 属性 logImpl

    • 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
    • 支持 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
    • 默认 未设置
  • 属性 proxyFactory

    • 指定 Mybatis 创建可延迟加载对象所用到的代理工具。
    • 支持 CGLIB | JAVASSIST
    • 默认 JAVASSIST (MyBatis 3.3 以上
  • 属性 vfsImpl

    • 指定 VFS 的实现
    • 支持 自定义 VFS 的实现的类全限定名,以逗号分隔。
    • 默认 未设置
  • 属性 useActualParamName

    • 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)
    • 支持 true | false
    • 默认 true
  • 属性 configurationFactory

    • 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)
    • 支持 一个类型别名或完全限定类名。
    • 默认 未设置
  • 属性 shrinkWhitespacesInSql

    • 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5)
    • 支持 true | false
    • 默认 false
  • 属性 defaultSqlProviderType

    • 指定一个本身拥查询方法的类( 从 3.5.6 开始 ),这个类可以配置在注解 @SelectProvider 的 type 属性值上。
    • 支持 一个类型别名或完全限定类名。
    • 默认 未设置

settings 支持了特别多功能支持,其实常规开发中使用到的属性项不会特别多,除非项目有特殊要求,所以建议大家把这些设置当做字典即可,不必详记 每一个属性使用,需要时翻阅研读。

3、typeAliases(类型别名)

类型别名可以给 Java 类型设置一个简称。 它仅用于 XML 配置,意在降低冗余的全限定类名书写,因为书写类的全限定名太长了,我们希望有一个简称来指代它。类型别名在 Mybatis 中分为 系统内置用户自定义 两类,Mybatis 会在解析配置文件时把 typeAliases 实例存储进入 Configuration 对象中,需要使用时直接获取。

一般我们可以自定义别名,例如:


  
   

像这样配置时,我们就可以在任何需要使用 domain.blog.Author 的地方,直接使用别名 author 。

但是,如果遇到项目中特别多 Java 类需要配置别名,怎么更快的设置呢?

可以指定一个包名进行扫描,MyBatis 会在包名下面扫描需要的 Java Bean,比如:


  

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有 注解 ,则别名为其自定义的注解值。见下面的例子:

@Alias("myAuthor")
public class Author {
    ...
}

Mybatis 已经为许多常见的 Java 类型内建了相应的类型别名。下面就是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格,可以发现 基本类型 的别名前缀都有下划线 ‘_’,而基本类型的 包装类 则没有,这个需要注意:

  • 别名 _byte,对应的类型是:byte
  • 别名 _long,对应的类型是:long
  • 别名 _short,对应的类型是:short
  • 别名 _int,对应的类型是:int
  • 别名 _integer,对应的类型是:int
  • 别名 _double,对应的类型是:double
  • 别名 _float,对应的类型是:float
  • 别名 _boolean,对应的类型是:boolean
  • 别名 string,对应的类型是:String
  • 别名 byte,对应的类型是:Byte
  • 别名 long,对应的类型是:Long
  • 别名 short,对应的类型是:Short
  • 别名 int,对应的类型是:Integer
  • 别名 integer,对应的类型是:Integer
  • 别名 double,对应的类型是:Double
  • 别名 float,对应的类型是:Float
  • 别名 boolean,对应的类型是:Boolean
  • 别名 date,对应的类型是:Date
  • 别名 decimal,对应的类型是:BigDecimal
  • 别名 bigdecimal,对应的类型是:BigDecimal
  • 别名 object,对应的类型是:Object
  • 别名 map,对应的类型是:Map
  • 别名 hashmap,对应的类型是:HashMap
  • 别名 list,对应的类型是:List
  • 别名 arraylist,对应的类型是:ArrayList
  • 别名 collection,对应的类型是:Collection
  • 别名 iterator,对应的类型是:Iterator

我们可以通过源码查看内置的类型别名的注册信息。

具体源码路径在 org.apache.ibatis.type.TypeAliasRegistry # TypeAliasRegistry() :

public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    registerAlias("ResultSet", ResultSet.class);
  }

别名是不区分大小写的,同时也支持数组类型,只需要加 “[]” 即可使用,比如 Long 数组别名我们可以用 long[] 直接代替,例如在实际开发中,int 、INT 、integer 、INTEGER 都是代表 Integer , 这里主要由于 MyBatis 在注册别名的时候会全部转为小写字母进行存储,另外以上列表 无需牢记,仅仅在需要使用的时候查阅即可,基本也都可以看得明白。

4、typeHandlers(类型处理器)

MyBatis 在设置预处理SQL语句(PreparedStatement)中所需要的 参数 或从 结果集 ResultSet 中获取对象时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。

类型处理器,主要用于处理 Java 类型与 JDBC 类型的映射匹配关系处理,下表描述了一些默认的类型处理器。

  • 类型处理器 BooleanTypeHandler
    • Java 类型:java.lang.Boolean, boolean
    • JDBC 类型:数据库兼容的 BOOLEAN
  • 类型处理器 ByteTypeHandler
    • Java 类型:java.lang.Byte, byte
    • JDBC 类型:数据库兼容的 NUMERIC 或 BYTE
  • 类型处理器 ShortTypeHandler
    • Java 类型:java.lang.Short, short
    • JDBC 类型:数据库兼容的 NUMERIC 或 SMALLINT
  • 类型处理器 IntegerTypeHandler
    • Java 类型:java.lang.Integer, int
    • JDBC 类型:数据库兼容的 NUMERIC 或 INTEGER
  • 类型处理器 LongTypeHandler
    • Java 类型:java.lang.Long, long
    • JDBC 类型:数据库兼容的 NUMERIC 或 BIGINT
  • 类型处理器 FloatTypeHandler
    • Java 类型:java.lang.Float, float
    • JDBC 类型:数据库兼容的 NUMERIC 或 FLOAT
  • 类型处理器 DoubleTypeHandler
    • Java 类型:java.lang.Double, double
    • JDBC 类型:数据库兼容的 NUMERIC 或 DOUBLE
  • 类型处理器 BigDecimalTypeHandler
    • Java 类型:java.math.BigDecimal
    • JDBC 类型:数据库兼容的 NUMERIC 或 DECIMAL
  • 类型处理器 StringTypeHandler
    • Java 类型:java.lang.String
    • JDBC 类型:CHAR, VARCHAR
  • 类型处理器 ClobReaderTypeHandler
    • Java 类型:java.io.Reader
    • JDBC 类型:-
  • 类型处理器 ClobTypeHandler
    • Java 类型:java.lang.String
    • JDBC 类型:CLOB, LONGVARCHAR
  • 类型处理器 NStringTypeHandler
    • Java 类型:java.lang.String
    • JDBC 类型:NVARCHAR, NCHAR
  • 类型处理器 NClobTypeHandler
    • Java 类型:java.lang.String
    • JDBC 类型:NCLOB
  • 类型处理器 BlobInputStreamTypeHandler
    • Java 类型:java.io.InputStream
    • JDBC 类型:-
  • 类型处理器 ByteArrayTypeHandler
    • Java 类型:byte[]
    • JDBC 类型:数据库兼容的字节流类型
  • 类型处理器 BlobTypeHandler
    • Java 类型:byte[]
    • JDBC 类型:BLOB, LONGVARBINARY
  • 类型处理器 DateTypeHandler
    • Java 类型:java.util.Date
    • JDBC 类型:TIMESTAMP
  • 类型处理器 DateonlyTypeHandler
    • Java 类型:java.util.Date
    • JDBC 类型:DATE
  • 类型处理器 TimeonlyTypeHandler
    • Java 类型:java.util.Date
    • JDBC 类型:TIME
  • 类型处理器 SqlTimestampTypeHandler
    • Java 类型:java.sql.Timestamp
    • JDBC 类型:TIMESTAMP
  • 类型处理器 SqlDateTypeHandler
    • Java 类型:java.sql.Date
    • JDBC 类型:DATE
  • 类型处理器 SqlTimeTypeHandler
    • Java 类型:java.sql.Time
    • JDBC 类型:TIME
  • 类型处理器 ObjectTypeHandler
    • Java 类型:Any
    • JDBC 类型:OTHER 或未指定类型
  • 类型处理器 EnumTypeHandler
    • Java 类型:Enumeration Type
    • JDBC 类型:VARCHAR 或任何兼容的字符串类型,用来存储枚举的名称(而不是索引序数值)
  • 类型处理器 EnumOrdinalTypeHandler
    • Java 类型:Enumeration Type
    • JDBC 类型:任何兼容的 NUMERIC 或 DOUBLE 类型,用来存储枚举的序数值(而不是名称)。
  • 类型处理器 SqlxmlTypeHandler
    • Java 类型:java.lang.String
    • JDBC 类型:SQLXML
  • 类型处理器 InstantTypeHandler
    • Java 类型:java.time.Instant
    • JDBC 类型:TIMESTAMP
  • 类型处理器 LocalDateTimeTypeHandler
    • Java 类型:java.time.LocalDateTime
    • JDBC 类型:TIMESTAMP
  • 类型处理器 LocalDateTypeHandler
    • Java 类型:java.time.LocalDate
    • JDBC 类型:DATE
  • 类型处理器 LocalTimeTypeHandler
    • Java 类型:java.time.LocalTime
    • JDBC 类型:TIME
  • 类型处理器 OffsetDateTimeTypeHandler
    • Java 类型:java.time.OffsetDateTime
    • JDBC 类型:TIMESTAMP
  • 类型处理器 OffsetTimeTypeHandler
    • Java 类型:java.time.OffsetTime
    • JDBC 类型:TIME
  • 类型处理器 ZonedDateTimeTypeHandler
    • Java 类型:java.time.ZonedDateTime
    • JDBC 类型:TIMESTAMP
  • 类型处理器 YearTypeHandler
    • Java 类型:java.time.Year
    • JDBC 类型:INTEGER
  • 类型处理器 MonthTypeHandler
    • Java 类型:java.time.Month
    • JDBC 类型:INTEGER
  • 类型处理器 YearMonthTypeHandler
    • Java 类型:java.time.YearMonth
    • JDBC 类型:VARCHAR 或 LONGVARCHAR
  • 类型处理器 JapaneseDateTypeHandler
    • Java 类型:java.time.chrono.JapaneseDate
    • JDBC 类型:DATE

我们可以通过源码查看内置的类型别名的注册信息。

具体源码路径在 org.apache.ibatis.type.TypeHandlerRegistry # TypeHandlerRegistry() :

 public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    // mybatis-typehandlers-jsr310
    if (Jdk.dateAndTimeApiExists) {
      Java8TypeHandlersRegistrar.registerDateAndTimeHandlers(this);
    }

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }

从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) ,可以在以上源码上看到新增支持。

一般,你可以重写已有的类型处理器

或根据业务需要创建你自己的类型处理器,

以处理不支持的类型或非标准的类型。

具体做法为:

1、实现 org.apache.ibatis.type.TypeHandler 接口;

2、继承 org.apache.ibatis.type.baseTypeHandler 类。

本身 baseTypeHandler 类作为抽象类就已经实现了 TypeHandler 接口。

所以我们看到接口 TypeHandler 定义了四个方法:

public interface TypeHandler {

  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

从方法名 setParametergetResult 我们就可以知道,是发生在预编译时设置参数(增删改查传入参数)与查询结果集后转换为 Java 类型时,类型处理器发挥作用。

具体实现如下,先自定义类型处理器类 MyExampleTypeHandler :

// MyExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyExampleTypeHandler extends baseTypeHandler {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter);
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    return cs.getString(columnIndex);
  }
}

自定义类已设定:JdbcType.VARCHAR 与 String 类做映射转换(注解和泛型已体现)。

其次,在核心配置文件中设置类型处理器:



  

或者不使用注解方式的话,取消 @MappedJdbcTypes(JdbcType.VARCHAR) 注解,直接在 xml 配置中指定 jdbcType 与 javaType 映射 :



  

记住, typeHandler 的配置方式优先级高于注解配置方式。

这里,自定义类型处理器将会覆盖已有的处理 Java String 类型的属性以及 VARCHAR 类型的参数和结果的类型处理器,基本以上步骤就已经自定了 JdbcType.VARCHARString类做映射转换。

其实到这里,我们基本也就完成了类型处理器的自定义转换,但是有一种情况,就是我们希望我们自定义的类型处理器只处理某一个 Java 实体中的 JdbcType.VARCHAR 与 String 类映射转换,其它实体的处理还是使用系统内置的转换,很简单,我们只需要把以上两步都去掉,在自定义类型处理类的注解@javaType和@MappedJdbcTypes都移除,配置文件中把 typehandler 属性配置移除,直接在映射文件中编写:

    
 
 
 
 
 
 
 
 
 
 
 
    
    
		select * from users
	
	
	    insert into users (id, name, funkyNumber, roundingMode) values (
	    	#{id}, #{name}, #{funkyNumber}, #{roundingMode}
	    )