MyBatis配置文件(四)--typeHandlers

摘要:
和别名一样,MyBatis中的类型处理器也存在系统定义的和自定义两种,MyBatis会根据javaType和jdbcType来决定采用哪个typeHandler来处理这些转换规则,而且系统定义的能满足大部分需求,可以说是非常好用,用户只需要自定义一些特有的转换规则,如枚举类型。下面分别介绍这两种typeHandler。

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 voidgetTypeHandlers() {
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.SqlTimeTypeHandler36 org.apache.ibatis.type.DoubleTypeHandler
37 org.apache.ibatis.type.ShortTypeHandler
38 org.apache.ibatis.type.LongTypeHandler
39 org.apache.ibatis.type.InstantTypeHandler
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 packageorg.apache.ibatis.type;
17 
18 importjava.sql.CallableStatement;
19 importjava.sql.PreparedStatement;
20 importjava.sql.ResultSet;
21 importjava.sql.SQLException;
22 
23 /**
24 * @authorClinton Begin
25  */
26 public class StringTypeHandler extends BaseTypeHandler<String>{
27 
28 @Override
29   public void setNonNullParameter(PreparedStatement ps, inti, String parameter, JdbcType jdbcType)
30       throwsSQLException {
31 ps.setString(i, parameter);
32 }
33 
34 @Override
35   publicString getNullableResult(ResultSet rs, String columnName)
36       throwsSQLException {
37     returnrs.getString(columnName);
38 }
39 
40 @Override
41   public String getNullableResult(ResultSet rs, intcolumnIndex)
42       throwsSQLException {
43     returnrs.getString(columnIndex);
44 }
45 
46 @Override
47   public String getNullableResult(CallableStatement cs, intcolumnIndex)
48       throwsSQLException {
49     returncs.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 packageorg.apache.ibatis.type;
17 
18 importjava.sql.CallableStatement;
19 importjava.sql.PreparedStatement;
20 importjava.sql.ResultSet;
21 importjava.sql.SQLException;
22 
23 importorg.apache.ibatis.executor.result.ResultMapException;
24 importorg.apache.ibatis.session.Configuration;
25 
26 /**
27 * @authorClinton Begin
28 * @authorSimone Tripodi
29  */
30 public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T>{
31 
32   protectedConfiguration configuration;
33 
34   public voidsetConfiguration(Configuration c) {
35     this.configuration =c;
36 }
37 
38 @Override
39   public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throwsSQLException {
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) throwsSQLException {
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       returnresult;
74 }
75 }
76 
77 @Override
78   public T getResult(ResultSet rs, int columnIndex) throwsSQLException {
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       returnresult;
89 }
90 }
91 
92 @Override
93   public T getResult(CallableStatement cs, int columnIndex) throwsSQLException {
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       returnresult;
104 }
105 }
106 
107   public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throwsSQLException;
108 
109   public abstract T getNullableResult(ResultSet rs, String columnName) throwsSQLException;
110 
111   public abstract T getNullableResult(ResultSet rs, int columnIndex) throwsSQLException;
112 
113   public abstract T getNullableResult(CallableStatement cs, int columnIndex) throwsSQLException;
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 packageorg.apache.ibatis.type;
17 
18 importjava.sql.CallableStatement;
19 importjava.sql.PreparedStatement;
20 importjava.sql.ResultSet;
21 importjava.sql.SQLException;
22 
23 /**
24 * @authorClinton Begin
25  */
26 public interface TypeHandler<T>{
27 
28   void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throwsSQLException;
29 
30   T getResult(ResultSet rs, String columnName) throwsSQLException;
31 
32   T getResult(ResultSet rs, int columnIndex) throwsSQLException;
33 
34   T getResult(CallableStatement cs, int columnIndex) throwsSQLException;
35 
36 }

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

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

第一种:继承BaseTypeHandler类

第二种:实现TypeHandler接口

二、自定义typeHandler

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

创建MyDateHandler

1 packagecom.daily.handler;
2 
3 importjava.sql.CallableStatement;
4 importjava.sql.PreparedStatement;
5 importjava.sql.ResultSet;
6 importjava.sql.SQLException;
7 importjava.text.SimpleDateFormat;
8 importjava.util.Date;
9 
10 importorg.apache.ibatis.type.DateTypeHandler;
11 importorg.apache.ibatis.type.JdbcType;
12 importorg.apache.ibatis.type.TypeHandler;
13 importorg.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) throwsSQLException {
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) throwsSQLException {
30         String columnValue =rs.getString(columnName);
31         if (null !=columnValue) {
32             return newDate(Long.valueOf(columnValue));
33 }
34         return null;
35 }
36 
37 @Override
38     //根据列名称从结果集获取值,并将jdbcType转换成javaType
39     public Date getResult(ResultSet rs, int columnIndex) throwsSQLException {
40         String columnValue =rs.getString(columnIndex);
41         if (null !=columnValue) {
42             return newDate(Long.valueOf(columnValue));
43 }
44         return null;
45 }
46 
47 @Override
48     public Date getResult(CallableStatement cs, int columnIndex) throwsSQLException {
49         String columnValue =cs.getString(columnIndex);
50         if (null !=columnValue) {
51             return newDate(Long.valueOf(columnValue));
52 }
53         return null;
54 }
55 
56 }

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

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

MyBatis配置文件(四)--typeHandlers第1张

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

1 packagecom.daily.pojo;
2 
3 importjava.util.Date;
4 
5 public classBlog {
6     privateInteger blogId;
7 
8     privateString blogTitle;
9 
10     privateString blogContent;
11 
12     privateDate createTime;
13 
14     privateDate modifyTime;
15 
16     publicInteger getBlogId() {
17         returnblogId;
18 }
19 
20     public voidsetBlogId(Integer blogId) {
21         this.blogId =blogId;
22 }
23 
24     publicString getBlogTitle() {
25         returnblogTitle;
26 }
27 
28     public voidsetBlogTitle(String blogTitle) {
29         this.blogTitle = blogTitle == null ? null: blogTitle.trim();
30 }
31 
32     publicString getBlogContent() {
33         returnblogContent;
34 }
35 
36     public voidsetBlogContent(String blogContent) {
37         this.blogContent = blogContent == null ? null: blogContent.trim();
38 }
39 
40     publicDate getCreateTime() {
41         returncreateTime;
42 }
43 
44     public voidsetCreateTime(Date createTime) {
45         this.createTime = createTime == null ? null: createTime;
46 }
47 
48     publicDate getModifyTime() {
49         returnmodifyTime;
50 }
51 
52     public voidsetModifyTime(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

MyBatis配置文件(四)--typeHandlers第2张

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

第二种:两个都不设置

MyBatis配置文件(四)--typeHandlers第3张

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

第三种:去掉MyDateHandler

MyBatis配置文件(四)--typeHandlers第4张

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

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

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

免责声明:文章转载自《MyBatis配置文件(四)--typeHandlers》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇SqlServer命名规范C# Emgucv3.0的安装与配置下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

JAVA通过Map拼接SQL语句(Insert Update语句)

packagecom.lynch.erp.core.util; importjava.util.Map; importorg.apache.commons.collections.MapUtils; importorg.apache.commons.lang3.StringUtils; public classSQLUtils {...

SpringMVC 使用验证框架 Bean Validation(上)

SpringMVC 使用验证框架 Bean Validation(上) 对于任何一个应用而言在客户端做的数据有效性验证都不是安全有效的,这时候就要求我们在开发的时候在服务端也对数据的有效性进行验证。 SpringMVC 自身对数据在服务端的校验(Hibernate Validator)有一个比较好的支持,它能将我们提交到服务端的数据按照我们事先的约定进行数...

Delphi 匿名函数的几个实际应用(二)

Timing Anonymous Methods 计时功能匿名函数   开发者们经常需要添加计时代码到一个已有的常规过程中,比较他们的相关速度。前面例子已经使用了几次实例,指出Uncode字符执行速度。对比两个代码片段,比较他们的执行毫秒数,妳可以写出下面内容(在本书第二章的“转换字符”例子):   [delphi]view plaincopy...

转 mysql mysql命令行中执行sql的几种方式总结

https://www.jb51.net/article/96394.htm 1.直接输入sql执行 MySQL> select now(); +---------------------+ | now() | +---------------------+ | 2013-09-18 13:55:45 | +---------------------...

使用Jackson的@JsonFormat注解时出现少了 8 个小时

比如数据库存的日期是2018-01-05,转成json则变成了2018-01-04 解决办法: @JsonFormat(pattern="yyyy-MM-dd") public Date getRegistDate() {  return this.registDate; } 改成 @JsonFormat(pattern="yyyy-MM-dd",tim...

工作笔记

更改过程让数据不时时更新 let add = JSON.stringify(editMarketChannel);this.editMarketChannel = JSON.parse(add); 这个为函数传入的对象,Obj 提示信息 成功的 this.msgs = [];this.msgs.push({severity:'success', summa...