在做开发时,我们经常会遇到这样一些问题,比如我有一个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!的更多相关文章

  1. 面试官问:Mybatis中的TypeHandler你用过吗?

    持续原创输出,点击上方蓝字关注我吧 目录 前言 环境配置 什么是TypeHandler? 如何自定义? 如何将其添加到Mybatis中? XML文件中如何指定TypeHandler? 源码中如何执行T ...

  2. 深入理解MyBatis中的一级缓存与二级缓存

    http://blog.csdn.net/weixin_36380516/article/details/73194758   先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓 ...

  3. 两个函数彻底理解Lua中的闭包

    本文通过两个函数彻底搞懂Lua中的闭包,相信看完这两个函数,应该能理解什么是Lua闭包.废话不多说,上 code: --[[************************************** ...

  4. 深入理解Mybatis中sqlSessionFactory机制原理

    对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章将通过以下几点详细介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XML配置 ...

  5. 轻松理解JS中的面向对象,顺便搞懂prototype和__proto__

    这篇文章主要讲一下JS中面向对象以及 __proto__,ptototype和construcator,这几个概念都是相关的,所以一起讲了. 在讲这个之前我们先来说说类,了解面向对象的朋友应该都知道, ...

  6. java工作两年了,连myBatis中的插件机制都玩不懂,那你工作危险了!

    插件的配置与使用 在mybatis-config.xml配置文件中配置plugin结点,比如配置一个自定义的日志插件LogInterceptor和一个开源的分页插件PageInterceptor: & ...

  7. 轻松理解JS中的面向对象,顺便搞懂prototype和__proto__的原理介绍

    这篇文章主要讲一下JS中面向对象以及 __proto__,ptototype和construcator,这几个概念都是相关的,所以一起讲了. 在讲这个之前我们先来说说类,了解面向对象的朋友应该都知道, ...

  8. 轻松理解python中的闭包和装饰器 (下)

    在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...

  9. 轻松理解python中的闭包和装饰器(上)

    继面向对象编程之后函数式编程逐渐火起来了,在python中也同样支持函数式编程,我们平时使用的map, reduce, filter等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...

随机推荐

  1. Requests库作者另一神器Pipenv的用法

    前言 我们在运行 Python 项目的时候经常会遇到一些版本问题,例如 A 项目依赖于 Django 1.5,而 B 项目又依赖 Django 2.0,而我们的系统却只有一个 Python 解释器,我 ...

  2. consoleWriter.go

    package blog4go import ( "fmt" "os" "time" ) // ConsoleWriter is a con ...

  3. 配置(迁移)Laravel的注意事项

    1.如果Laravel是在Linux下运行,如果权限不足,会报错 2.如果是从git上clone下来的项目,需要安装composer,切到项目根目录下 composer install compose ...

  4. js的赋值问题:值传递还是引用传递?

    ECMAScript中有5种简单数据类型(也称为基本数据类型):Undefined.Null.Boolean.Number和String.还有1种复杂数据类型--Object,Object本质上是由一 ...

  5. 数字类型——python3

    今天我为各位小伙伴准备了python3中数字类型,希望能够帮助到你们! Python 数字数据类型用于存储数值. 数据类型是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间. 以下 ...

  6. 不使用SpringBoot如何将原生Feign集成到Spring中来简化http调用

    在微服务架构中,如果使用得是SpringCloud,那么只需要集成SpringFeign就可以了,SpringFeign可以很友好的帮我们进行服务请求,对象解析等工作. 然而SpingCloud是依赖 ...

  7. EffictiveC++笔记 第1章

    Chapter 一: 条款 1 :视 C++为一个语言联邦 (P41 ) c++其实可以视为有四个部分: C Object-Oriented C++ Template C++ STL 条款 2:尽量以 ...

  8. 一个C#程序员学习微信小程序路由的笔记

    路由大家应该都知道,在微信小程序也是有的,毕竟它是单页面应用程序.在WeChat中有五种跳转方式,分别是wx.switchTab.wx.reLaunch.wx.redirectTo.wx.naviga ...

  9. Eclipse Debug调试遇到的问题

    在使用Debug模式前,一定要先打好断点.快捷调试方法:   在使用过程中,可以只保留Console,Variables,Debug窗口,把其他无用的窗口最小化掉,或者删掉即可.如果要恢复删掉的试图, ...

  10. unison+inotify的Web目录同步方案

    1.在Linux下做WEB目录文件同步 一般有如下几种方式: ----------------------------------------------- 1)       nfs实现web数据共享 ...