两个案例轻松理解MyBatis中的TypeHandler!
在做开发时,我们经常会遇到这样一些问题,比如我有一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,怎么实现?再比如我有一个User类,User类中有一个属性叫做interest,这个属性用来描述用户的爱好,它的数据类型是一个List集合,那么我想在把这个List集合存入数据库的时候能够自动的变成{XXX,XXX,XXX}这样一个字符串然后存起来,当我从数据库读取的时候也是读取到这样一个字符串,读取成功之后再自动的将之转为一个List集合,OK,以上两种需求用我们传统的数据库读写操作肯定都是可以实现的,只不过工作量略大,在mybatis中有一个功能略强大的typeHandler专门用来解决数据库中的数据类型和Java中的数据类型之间的转化问题,那么我们今天以上面两种需求为例,来看看typeHandler要怎么使用。如果还有小伙伴对mybatis不太熟悉,建议先阅读一下前面几篇博客(初识mybatis/初识mybatis(二)/mybatis常用配置/mybatis映射器配置细则),本文的内容将在前面几篇博客的基础上展开,当然如果小伙伴有mybatis基础,那直接往下看即可。
事实上,mybatis本身已经为我们提供了许多typeHandler了,系统提供的typeHandler能够满足我们日常开发中的大部分需求,如上这两种特殊的需求就需要我们自己去定义typeHandler了。
日期的转换
先来看日期的转换,假设我现在创建一张表,如下:
这张表中有一个字段叫做regTime,这个字段表示用户的注册时间,它的数据类型为varchar,OK,然后我再在Java中定义一个实体类:
public class User {
private Long id;
private String username;
private String password;
private Date regTime;
//省略getter/setter
}
这个JavaBean中也有一个regTime字段,不同的是这里的数据类型是Date,OK,如果我不做任何特殊处理,直接像初识mybatis(二)这篇博客中介绍的那样向数据库插入数据,也是可以插入成功的,但是插入成功后是这样:
这个当然不是我想要的,我希望存到数据库里的是这样的:
就是我直接向数据库写数据,要写的是一个Date对象,但是写到数据库之后这个Date对象就变成了Date对象所描述的时间到1970年的秒数了,然后当我从数据库读取这个秒数之后,系统又会自动帮我将这个秒数转为Date对象,就是这样两个需求。这个时候,我们要做的事情其实很简单,那就是自定义typeHandler,自定义typeHandler我们有两种方式,一种是实现TypeHandler接口,还有一种简化的写法就是继承自BaseTypeHandler类,我这里先以第二种为例来进行说明。
自定义typeHandler继承自BaseTypeHandler
@MappedJdbcTypes({JdbcType.VARCHAR})
@MappedTypes({Date.class})
public class MyDateTypeHandler extends BaseTypeHandler<Date> {
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i, String.valueOf(date.getTime()));
}
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
return new Date(resultSet.getLong(s));
}
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
return new Date(resultSet.getLong(i));
}
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return callableStatement.getDate(i);
}
}
关于这个类我说如下几点:
1.@MappedJdbcTypes定义的是JdbcType类型,这里的类型不可自己随意定义,必须要是枚举类org.apache.ibatis.type.JdbcType所枚举的数据类型。
2.@MappedTypes定义的是JavaType的数据类型,描述了哪些Java类型可被拦截。
3.在我们启用了我们自定义的这个TypeHandler之后,数据的读写都会被这个类所过滤
4.在setNonNullParameter方法中,我们重新定义要写往数据库的数据。
5.在另外三个方法中我们将从数据库读出的数据类型进行转换。
在Mapper中进行配置
自定义好了typeHandler之后,接下来我们需要在userMapper.xml中进行简单的配置,首先我们可以像上文说的,配置resultMap,如下:
<resultMap id="userResultMap" type="org.sang.bean.User">
<result typeHandler="org.sang.db.MyDateTypeHandler" column="regTime" javaType="java.util.Date"
jdbcType="VARCHAR"
property="regTime"/>
</resultMap>
配置resultMap的时候我们指定了javaType和jdbcType,同时也指定了处理的typeHandler,然后在select中使用这个resultMap:
<select id="getUser" resultMap="userResultMap">
select * from user4
</select>
但是这种方式有一个缺点那就是只适用于查询操作,即在查询的过程中系统会启用我们自定义的typeHandler,会将秒数转为Date对象,但是在插入的时候却不会启用我们自定义的typeHandler,想要在插入的时候启用自定义的typeHandler,需要我们在insert节点中简单配置一下,如下:
<insert id="insertUser" parameterType="org.sang.bean.User">
INSERT INTO user4(username,password,regTime) VALUES (#{username},#{password},#{regTime,javaType=Date,jdbcType=VARCHAR,typeHandler=org.sang.db.MyDateTypeHandler})
</insert>
也可以只配置javaType和jdbcType,如下:
<insert id="insertUser2">
INSERT INTO user4(username,password,regTime) VALUES (#{username},#{password},#{regTime,javaType=Date,jdbcType=VARCHAR})
</insert>
或者只配置typeHandler:
<insert id="insertUser3">
INSERT INTO user4(username,password,regTime) VALUES (#{username},#{password},#{regTime,typeHandler=org.sang.db.MyDateTypeHandler})
</insert>
这三种效果都是一样的,都是在插入的时候将数据Date对象转为秒数。OK,如此之后,我们就可以实现将Date对象插入数据库之后变秒数以及将数据库中的秒数读取之后自动转为Date对象了。我们来看一个简单的测试:
@Test
public void test2() {
SqlSession sqlSession = null;
try {
sqlSession = DBUtils.openSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setPassword("222222");
user.setUsername("李四");
Date regTime = new Date();
user.setRegTime(regTime);
userMapper.insertUser(user);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
插入结果如下:
读取代码:
@Test
public void test1() {
SqlSession sqlSession = null;
try {
sqlSession = DBUtils.openSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.getUser();
for (User user : list) {
System.out.println(user);
}
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
读取结果如下:
OK,结果上面几步配置我们就完美的解决了读写时的数据转换问题了,读取时的数据转换除了我们上面介绍的定义resultMap然后在select节点中引用这种方式之外,也可以使用下面这种方式,注意下面这种方式只能解决读取时的数据转换问题
在配置文件中注册typeHandler
我们需要在我们的mybatis配置文件中注册typeHandler,注册有两种不同的方式,可以像下面这样一个类一个类的注册:
<typeHandlers>
<typeHandler handler="org.sang.db.MyDateTypeHandler"/>
</typeHandlers>
也可以直接注册一个包中所有的typeHandler,系统在启动时会自动扫描包下的所有文件,如下:
<typeHandlers>
<package name="org.sang.db"/>
</typeHandlers>
这样配置完成之后,我们的目的就达到了,当我们进行数据库的读取操作的时候,秒数就会自动转为Date对象。
小结
OK,经过上面的介绍,想必小伙伴对typeHandler的使用已经有一定了解了,总结一下就是读取时的配置要和插入时的配置分贝来做,读取时数据转换我们有两种配置方式,分别是resultMap和在mybatis配置文件中配置typeHandlers,插入时的配置就是在insert节点中进行配置。
List集合的转换
OK,如果小伙伴们学会了如何把Date转为秒数,那么对于List集合的转换我就不再赘述了,道理都是一样的,大家可以可以直接在文末下载Demo,Demo中有List集合转换的案例。
这里给大家看两张效果图吧:
List集合存入数据库之后变成这样:
读取出来之后又自动转为List集合了,下图是查询操作,实体类,和查询结果:
OK,以上就是我们对typeHandler的一个简单介绍。
本文案例下载:
本文案例GitHub地址https://github.com/lenve/JavaEETest/tree/master/Test27-mybatis6。
以上。
关注公众号【江南一点雨】,专注于 Spring Boot+微服务以及前后端分离等全栈技术,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

参考资料:
《深入浅出MyBatis 技术原理与实战》第三章
两个案例轻松理解MyBatis中的TypeHandler!的更多相关文章
- 面试官问:Mybatis中的TypeHandler你用过吗?
持续原创输出,点击上方蓝字关注我吧 目录 前言 环境配置 什么是TypeHandler? 如何自定义? 如何将其添加到Mybatis中? XML文件中如何指定TypeHandler? 源码中如何执行T ...
- 深入理解MyBatis中的一级缓存与二级缓存
http://blog.csdn.net/weixin_36380516/article/details/73194758 先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓 ...
- 两个函数彻底理解Lua中的闭包
本文通过两个函数彻底搞懂Lua中的闭包,相信看完这两个函数,应该能理解什么是Lua闭包.废话不多说,上 code: --[[************************************** ...
- 深入理解Mybatis中sqlSessionFactory机制原理
对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章将通过以下几点详细介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XML配置 ...
- 轻松理解JS中的面向对象,顺便搞懂prototype和__proto__
这篇文章主要讲一下JS中面向对象以及 __proto__,ptototype和construcator,这几个概念都是相关的,所以一起讲了. 在讲这个之前我们先来说说类,了解面向对象的朋友应该都知道, ...
- java工作两年了,连myBatis中的插件机制都玩不懂,那你工作危险了!
插件的配置与使用 在mybatis-config.xml配置文件中配置plugin结点,比如配置一个自定义的日志插件LogInterceptor和一个开源的分页插件PageInterceptor: & ...
- 轻松理解JS中的面向对象,顺便搞懂prototype和__proto__的原理介绍
这篇文章主要讲一下JS中面向对象以及 __proto__,ptototype和construcator,这几个概念都是相关的,所以一起讲了. 在讲这个之前我们先来说说类,了解面向对象的朋友应该都知道, ...
- 轻松理解python中的闭包和装饰器 (下)
在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...
- 轻松理解python中的闭包和装饰器(上)
继面向对象编程之后函数式编程逐渐火起来了,在python中也同样支持函数式编程,我们平时使用的map, reduce, filter等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...
随机推荐
- bzoj 1901 主席树+树状数组
修改+查询第k小值 单纯主席树修改会打乱所有,所以再套一个树状数组维护前缀和使得修改,查询都是log 对了,bzoj上不需要读入组数,蜜汁re.. #include<cstdio> #in ...
- BZOJ_3670_[Noi2014]动物园_KMP
BZOJ_3670_[Noi2014]动物园_KMP Description 近日,园长发现动物园中好吃懒做的动物越来越多了.例如企鹅,只会卖萌向游客要吃的.为了整治动物园的不良风气,让动物们凭自己的 ...
- solr+jieba结巴分词
为什么选择结巴分词 分词效率高 词料库构建时使用的是jieba (python) 结巴分词Java版本 下载 git clone https://github.com/huaban/jieba-ana ...
- MyBatis新手教程(一)
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache 迁移到了 google,并改名为MyBatis,2013年迁移到Github. MyBatis是一个优秀的持 ...
- MySQL-5.6.36-多实例-部署(编译版)
MySQL多实例_沁贰百科 注:部署双实例前,首先需要部署单实例,单实例部署详情如下: https://www.cnblogs.com/wangqiner/p/9081002.html 1.如已经安装 ...
- monkey------模块组合测试
由于项目基本功能和预置APK都很多,单个模块跑消耗机器数量很大,效果也不佳.而且monkey测试经常要过夜测试,所以组合模块试用较多,而且发现问题量也更大.组合模块就是按照测试标准要求和模块特性,按照 ...
- [区块链] 共识算法之争(PBFT,Raft,PoW,PoS,DPoS,Ripple)
近几天对区块链中几种常见的共识机制(PBFT,Raft,PoW,PoS,DPoS,Ripple)进行了总结.尽量使用简单易懂语言,篇幅较大,想了解的可以只读每个算法介绍中前边的原理.本篇文章主要参考& ...
- 百度病了,必应挂了,Yandex疯了。
前天一篇<搜索引擎百度已死>的文章火遍了互联网圈.文中作者指出如今的百度搜索首页一大半都是百度自家的产品,比如你搜索特普朗,你会发现第一页的结果分别是:百度百科.贴吧.百家号.百家号.百家 ...
- 【转】javascript笔记之apply、call、bind用法
原文地址:https://www.cnblogs.com/coco1s/p/4833199.html apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数 ...
- Spring WebFlux开门迎客,却来了一位特殊客人
话说Spring WebFlux已经出现有一段时间了,但是知道他的人并不是很多.这让他很是闷闷不乐. 还有更惨的是,那些敢于吃螃蟹的人在尝试了他之后,有的竟把代码重新改回到Spring MVC的同步模 ...