关于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。
以上。
参考资料:
《深入浅出MyBatis 技术原理与实战》第三章
关注公众号【江南一点雨】,专注于 Spring Boot+微服务以及前后端分离等全栈技术,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

关于mybatis中typeHandler的两个案例的更多相关文章
- mybatis中的高级查询
Mybatis作为一个ORM框架,肯定是支持sql高级查询的. 下面的一个案例来为大家详细讲解mybatis中的高级查询. 案例说明: 此案例的业务关系是用户,订单,订单详情与商品之间的关系. 以订单 ...
- Mybatis中的缓存
Mybatis提供缓存查询功能,用于减轻数据库压力,提升数据查询能力. Mybatis中定义了两级缓存:包括一级缓存与二级缓存.示意图如下所示: 一.一级缓存 一级缓存的特点: 每一个SqlSessi ...
- 浅析mybatis中${}和#{}取值区别
mybatis作为一个轻量级的ORM框架,应用广泛,其上手使用也比较简单:一个成熟的框架,必然有精巧的设计,值得学习. 在使用mybatis框架时,在sql语句中获取传入的参数有如下两种方式: ${p ...
- mybatis中两种取值方式?谈谈Spring框架理解?
1.mybatis中两种取值方式? 回答:Mybatis中取值方式有几种?各自区别是什么? Mybatis取值方式就是说在Mapper文件中获取service传过来的值的方法,总共有两种方式,通过 $ ...
- 面试官问:Mybatis中的TypeHandler你用过吗?
持续原创输出,点击上方蓝字关注我吧 目录 前言 环境配置 什么是TypeHandler? 如何自定义? 如何将其添加到Mybatis中? XML文件中如何指定TypeHandler? 源码中如何执行T ...
- MyBatis中主键回填的两种实现方式
主键回填其实是一个非常常见的需求,特别是在数据添加的过程中,我们经常需要添加完数据之后,需要获取刚刚添加的数据 id,无论是 Jdbc 还是各种各样的数据库框架都对此提供了相关的支持,本文我就来和和大 ...
- 使用mybatis中的自定义TypeHandler处理PostgreSQL中的Json类型字段
业务扩展字段在PostgreSQL数据库中经常会使用json格式的数据来存储,然而mybatis默认是没有实现json类型字段对应的TypeHandler,所以一般我们需要自定义mybatis的Typ ...
- MyBatis 中两表关联查询MYSQL (14)
MyBatis 中两表关联查询MYSQL 1.创建数据库表语句 2.插入测试数据 3.pom文件内容 <?xml version="1.0" encoding="U ...
- mybatis中批量插入的两种方式(高效插入)
MyBatis简介 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用 ...
随机推荐
- GDB 调试 C++ 程序 core dump
https://blog.csdn.net/yockie/article/details/51973740
- Annotation(注解)
注解相当于一种标记,在程序中加入注解就相当于为程序打上某种标记,没有加,则表示没有任何标记,以后,javac编译器.开发工具和其它程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么 ...
- 为什么在Python里推荐使用多进程而不是多线程?
最近在看Python的多线程,经常我们会听到老手说:“Python下多线程是鸡肋,推荐使用多进程!”,但是为什么这么说呢? 要知其然,更要知其所以然.所以有了下面的深入研究: 首先强调背景: 1. ...
- [微信跳转链接]之WAP跳转微信内指定页面
由于微信覆盖太全面了,几乎所有人都使用微信APP,但是对于做产品的公司来说,所有的产品几乎都离不开微信的推广,然而微信属于封闭式的一个社交应用,大部分只能在今日头条,百度,等等...如果你在推广页面上 ...
- js 原生转json 可以v8中运行
// load("D:/jsontest.js"); function test1(vvv) { print(vvv); } //把json str 转 json obj func ...
- PHP 清除 Excel 导入的数据空格
处理excel中的数据时,遇到了页面中显示为空格,审查元素时却显示为换行,使用replace函数也不管用,反正就是不知道是什么东西,看起来像空格 中文空格这里面有好几种:没有简单的解决问题的方式,比如 ...
- 引用Excel控件时,无法嵌入互操作类型“Microsoft.Office.Interop.Excel.ApplicationClass”请改用适用的接口
类型Microsoft.Office.Interop.Excel.ApplicationClass未定义构造函数 无法嵌入互操作类型“Microsoft.Office.Interop.Excel.Ap ...
- 网防G01管理检测系统Linux版安装
监测包内容: gov_defence_agent_x64_linux_v3.1.18.tar.gz LinuxVersion(datalog.sh getlog.sh setup.sh) 1. ...
- ActiveMQ队列、主题模式区别
1.ActiveMQ队列模式如下图,生产者创建消息到消息中间件,再“均分给消费者”. 2.ActiveMQ主题模式如下图,生产者创建消息到消息中间件,消费者会接受到订阅的主题中所有的消息.在主题模式下 ...
- 可道云kodexplorer网盘未清理造成linux服务器爆满的解决方法
今天登陆宝塔面板的时候发现硬盘占用37GB,已经变红提示我空间不足了,惊呆了, 还以为是宝塔抽风了,去远程连接服务器看了一下,懵逼了. df -h 查看挂载目录使用情况 还是不相信现实的我又重启了一下 ...