建立TypeHandler

我们知道java有java的数据类型,数据库有数据库的数据类型,那么我们在往数据库中插入数据的时候是如何把java类型当做数据库类型插入数据库,在从数据库读取数据的时候又是如何把数据库类型当做java类型来处理呢?这中间必然要经过一个类型转换。在Mybatis中我们可以定义一个叫做TypeHandler类型处理器的东西,通过它可以实现Java类型跟数据库类型的相互转换。下面将就如何建立自己的TypeHandler做一个简要介绍。

TypeHandler接口

在Mybatis中要实现自己的TypeHandler就需要实现Mybatis为我们提供的TypeHandler接口。在TypeHandler中定义了四个方法:

public interface TypeHandler<T> {  

    /** 

     * 用于定义在Mybatis设置参数时该如何把Java类型的参数转换为对应的数据库类型 

     * @param ps 当前的PreparedStatement对象 

     * @param i 当前参数的位置 

     * @param parameter 当前参数的Java对象 

     * @param jdbcType 当前参数的数据库类型 

     * @throws SQLException 

     */  

    void setParameter(PreparedStatement ps, int i, T parameter,  

           JdbcType jdbcType) throws SQLException;  

    /** 

     * 用于在Mybatis获取数据结果集时如何把数据库类型转换为对应的Java类型 

     * @param rs 当前的结果集 

     * @param columnName 当前的字段名称 

     * @return 转换后的Java对象 

     * @throws SQLException 

     */  

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

    /** 

     * 用于在Mybatis通过字段位置获取字段数据时把数据库类型转换为对应的Java类型 

     * @param rs 当前的结果集 

     * @param columnIndex 当前字段的位置 

     * @return 转换后的Java对象 

     * @throws SQLException 

     */  

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

    /** 

     * 用于Mybatis在调用存储过程后把数据库类型的数据转换为对应的Java类型 

     * @param cs 当前的CallableStatement执行后的CallableStatement 

     * @param columnIndex 当前输出参数的位置 

     * @return 

     * @throws SQLException 

     */  

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

}  

现在假设我们有一个实体对象User,其中有一个属性interests是String数组类型,如下所示:

public class User {  

    private int id;  

    private String name;  

    private int age;  

    private String[] interests;  

    public int getId() {  

       return id;  

    }  

    public void setId(int id) {  

       this.id = id;  

    }  

    public String getName() {  

       return name;  

    }  

    public void setName(String name) {  

       this.name = name;  

    }  

    public int getAge() {  

       return age;  

    }  

    public void setAge(int age) {  

       this.age = age;  

    }  

    public String[] getInterests() {  

       return interests;  

    }  

    public void setInterests(String[] interests) {  

       this.interests = interests;  

    }  

    @Override  

    public String toString() {  

       return "User [age=" + age + ", id=" + id + ", interests="  

              + Arrays.toString(interests) + ", name=" + name + "]";  

    }  

}  

我们需要把它以拼接字符串的形式存到数据库中,然后在取出来的时候又把它还原为一个String数组。这个时候我们就可以给它定义一个TypeHandler专门来处理String数组类型和数据库VARCHAR类型的相互转换。在这里我们建立一个名叫StringArrayTypeHandler的TypeHandler,代码如下所示:

package com.tiantian.mybatis.handler;  

import java.sql.CallableStatement;  

import java.sql.PreparedStatement;  

import java.sql.ResultSet;  

import java.sql.SQLException;  

import java.sql.Types;  

import org.apache.ibatis.type.JdbcType;  

import org.apache.ibatis.type.TypeHandler;  

public class StringArrayTypeHandler implements TypeHandler<String[]> {  

       public String[] getResult(ResultSet rs, String columnName)  

                     throws SQLException {  

              String columnValue = rs.getString(columnName);  

              return this.getStringArray(columnValue);  

       }  

       public String[] getResult(ResultSet rs, int columnIndex)  

                     throws SQLException {  

              String columnValue = rs.getString(columnIndex);  

              return this.getStringArray(columnValue);  

       }  

       public String[] getResult(CallableStatement cs, int columnIndex)  

                     throws SQLException {  

              // TODO Auto-generated method stub  

              String columnValue = cs.getString(columnIndex);  

              return this.getStringArray(columnValue);  

       }  

       public void setParameter(PreparedStatement ps, int i, String[] parameter,  

                     JdbcType jdbcType) throws SQLException {  

              if (parameter == null)  

                     ps.setNull(i, Types.VARCHAR);  

              else {  

                     StringBuffer result = new StringBuffer();  

                     for (String value : parameter)  

                            result.append(value).append(",");  

                     result.deleteCharAt(result.length()-1);  

                     ps.setString(i, result.toString());  

              }  

       }  

       private String[] getStringArray(String columnValue) {  

              if (columnValue == null)  

                     return null;  

              return columnValue.split(",");  

       }  

}  

BaseTypeHandler抽象类

在实现自己的TypeHandler时,除了上面提到的实现最原始的接口之外,Mybatis还为我们提供了一个实现了TypeHandler接口的抽象类BaseTypeHandler。所以我们也可以通过继承BaseTypeHandler来实现自己的TypeHandler。

我们先来看一下BaseTypeHandler类的定义:

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {  

  protected Configuration configuration;  

  public void setConfiguration(Configuration c) {  

    this.configuration = c;  

  }  

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

    if (parameter == null) {  

      if (jdbcType == null) {  

        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");  

      }  

      try {  

        ps.setNull(i, jdbcType.TYPE_CODE);  

      } catch (SQLException e) {  

        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +  

             "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +  

             "Cause: " + e, e);  

      }  

    } else {  

      setNonNullParameter(ps, i, parameter, jdbcType);  

    }  

  }  

  public T getResult(ResultSet rs, String columnName) throws SQLException {  

    T result = getNullableResult(rs, columnName);  

    if (rs.wasNull()) {  

      return null;  

    } else {  

      return result;  

    }  

  }  

  public T getResult(ResultSet rs, int columnIndex) throws SQLException {  

    T result = getNullableResult(rs, columnIndex);  

    if (rs.wasNull()) {  

      return null;  

    } else {  

      return result;  

    }  

  }  

  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {  

    T result = getNullableResult(cs, columnIndex);  

    if (cs.wasNull()) {  

      return null;  

    } else {  

      return result;  

    }  

  }  

  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;  

  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;  

  public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;  

  public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;  

}  

我们可以看到BaseTypeHandler对TypeHandler接口的四个方法做了一个简单的选择,把null值的情况都做了一个过滤,核心的取值和设值的方法还是抽象出来了供子类来实现。使用BaseTypeHandler还有一个好处是它继承了另外一个叫做TypeReference的抽象类,通过TypeReference的getRawType()方法可以获取到当前TypeHandler所使用泛型的原始类型。这对Mybatis在注册TypeHandler的时候是非常有好处的。在没有指定javaType的情况下,Mybatis在注册TypeHandler时可以通过它来获取当前TypeHandler所使用泛型的原始类型作为要注册的TypeHandler的javaType类型,这个在讲到Mybatis注册TypeHandler的方式时将讲到。

当通过继承BaseTypeHandler来实现自己的TypeHandler时,我们的StringArrayTypeHandler应该这样写:

public class StringArrayTypeHandler extends BaseTypeHandler<String[]> {  

    @Override  

    public String[] getNullableResult(ResultSet rs, String columnName)  

           throws SQLException {  

       return getStringArray(rs.getString(columnName));  

    }  

    @Override  

    public String[] getNullableResult(ResultSet rs, int columnIndex)  

           throws SQLException {  

       return this.getStringArray(rs.getString(columnIndex));  

    }  

    @Override  

    public String[] getNullableResult(CallableStatement cs, int columnIndex)  

           throws SQLException {  

       return this.getStringArray(cs.getString(columnIndex));  

    }  

    @Override  

    public void setNonNullParameter(PreparedStatement ps, int i,  

           String[] parameter, JdbcType jdbcType) throws SQLException {  

       //由于BaseTypeHandler中已经把parameter为null的情况做了处理,所以这里我们就不用再判断parameter是否为空了,直接用就可以了  

       StringBuffer result = new StringBuffer();  

       for (String value : parameter)  

           result.append(value).append(",");  

       result.deleteCharAt(result.length()-1);  

       ps.setString(i, result.toString());  

    }  

    private String[] getStringArray(String columnValue) {  

       if (columnValue == null)  

           return null;  

       return columnValue.split(",");  

    }  

}

注册TypeHandler

建立了自己的TypeHandler之后就需要把它注册到Mybatis的配置文件中,让Mybatis能够识别并使用它。注册TypeHandler主要有两种方式,一种是通过在Mybatis配置文件中定义typeHandlers元素的子元素typeHandler来注册;另一种是通过在Mybatis配置文件中定义typeHandlers元素的子元素package来注册。使用typeHandler子元素注册时一次只能注册一个TypeHandler,而使用package子元素注册时,Mybatis会把指定包里面的所有TypeHandler都注册为TypeHandler。使用typeHandler子元素注册时我们需要通过它的handler属性来指明当前要注册的TypeHandler的全名称,这个属性是必须要的。另外还有两个附加属性可以指定,一个是javaType,用以指定对应的java类型;另一个是jdbcType,用以指定对应的jdbc类型。使用package子元素注册时需要我们通过它的name属性来指定要扫描的包,如果这个时候我们也需要指定对应TypeHandler的javaType和jdbcType的话就需要我们在TypeHandler类上使用注解来定义了。Mybatis注册TypeHandler最基本的方式就是建立一个javaType、jdbcType和TypeHandler的对应关系。在使用typeHandler子元素进行注册的时候,有三种类型的注册方式:

1.如果我们指定了javaType和jdbcType,那么Mybatis会注册一个对应javaType和jdbcType的TypeHandler。

2.如果我们只指定了javaType属性,那么这个时候又分两种情况:

(1)如果我们通过注解的形式在TypeHandler类上用@MappedJdbcTypes指定了对应的jdbcType,那么Mybatis会一一注册指定的javaType、jdbcType和TypeHandler的组合,也包括使用这种形式指定了jdbcType为null的情况。现假设我们有如下这样一个StringArrayTypeHandler:

@MappedJdbcTypes({JdbcType.VARCHAR})  

public class StringArrayTypeHandler implements TypeHandler<String[]> {  

    //..中间的实现代码省略了  

    //..  

}  

然后我们在Mybatis的配置文件中这样注册它:

<typeHandlers>  

   <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/>  

</typeHandlers>  

则Mybatis在实际注册的时候是以javaType为String数组,jdbcType为VARCHAR来注册StringArrayTypeHandler的。

mybatis typehandler的更多相关文章

  1. mybatis typeHandler类型转换器

    typeHandler类型转换器 在JDBC中,需要在PreparedStatement对象中设置那些已经预编译过的SQL语句的参数.执行SQL后,会通过ResultSet对象获取得到数据库的数据,而 ...

  2. mybatis基础,mybatis配置文件核心组件typeHandler元素

    无论是从预处理语句中设置一个值,还是从结果集里取出一个值,都会用类型处理器将获取的值以合适的方式转换成 Java 类型 可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型 实现 o ...

  3. mybatis 枚举typeHandler

    枚举typeHandler 在绝大多数情况下,typeHandler因为枚举而使用,MyBatis已经定义了两个类作为枚举类型的支持,这两个类分别是: •EnumOrdinalTypeHandler. ...

  4. 使用Mybatis的TypeHandler加解密数据

    使用Mybatis的TypeHandler加解密数据 一.背景 二.解决方案 三.需求 四.实现思路 1.编写一个实体类,凡是此实体类的数据都表示需要加解密的 2.编写一个加解密的`TypeHandl ...

  5. TypeHandler的简单实例

    转自:http://ccchhhlll1988-163-com.iteye.com/blog/1420149 TypeHandler是MyBatis config文件中可选的配置选项,其可以对实体属性 ...

  6. MyBatis里字段到枚举类型的转换/映射

    一.简介 我们在用MyBatis里,很多时间有这样一个需求:bean里有个属性是枚举,在DB存储时我们想存的枚举的代号,从DB拿出来时想直接映射成目标枚举类型,也即代号字段与Java枚举类的相互类型转 ...

  7. MyBatis里json型字段到Java类的映射

    一.简介 我们在用MyBatis里,很多时间有这样一个需求:bean里有个属性是非基本数据类型,在DB存储时我们想存的是json格式的字符串,从DB拿出来时想直接映射成目标类型,也即json格式的字符 ...

  8. mybatis(二)

    一级缓存和二级缓存 mybatis一二级缓存测试实例: package com.atguigu.mybatis.test; import java.io.IOException; import jav ...

  9. 深入理解MyBatis的原理(三):配置文件用法(续)

    前言:前文讲解了 MyBatis 的配置文件一部分用法,本文将继续讲解 MyBatis 的配置文件的用法. 目录 1.typeHandler 类型处理器 2.ObjectFactory 3.插件 4. ...

随机推荐

  1. 流畅python学习笔记:第十九章:动态属性和特性

    首先来看一个json文件的读取.书中给出了一个json样例.该json文件有700多K,数据量充足,适合本章的例子.文件的具体内容可以在http://www.oreilly.com/pub/sc/os ...

  2. Python之set

    set set集合,是一个无序且不重复的元素集合 set的优势 set 的访问数度快 set 原生解决数据重复问题 # 数据库中原有 old_dict = { "#1":{ 'ho ...

  3. XMind入门教程

    最近在总结一些框架知识的时候,总找不到一款好的软件来画流程图,后来在网上查找这方面的东西,找到了 XMind,发现用来画思维导图还挺好的,看起来思路清晰,美观.那么便将使用的一些经验分享给大家. 1. ...

  4. spring boot / cloud (十六) 分布式ID生成服务

    spring boot / cloud (十六) 分布式ID生成服务 在几乎所有的分布式系统或者采用了分库/分表设计的系统中,几乎都会需要生成数据的唯一标识ID的需求, 常规做法,是使用数据库中的自动 ...

  5. chrome开发工具指南(七)

    检查动画 使用 Chrome DevTools 动画检查器检查和修改动画. 通过打开动画检查器捕捉动画.检查器会自动检测动画并将它们分类为多个组. 通过慢速播放.重播或查看动画源代码来检查动画. 通过 ...

  6. input[type="button"]与<button>的区别

    <button>标签  浏览器支持  所有主流浏览器都支持<button>标签.  重要事项:如果在HTML表单中使用button元素,不同的浏览器会提交不同的值.IE将提交& ...

  7. POJ3228 并查集或二分最大流枚举答案

    忘记写题意了.这题题意:给出每个地点的金矿与金库的数量,再给出边的长度.求取最大可通过边长的最小权值使每个金矿都能运输到金库里. 这题和之前做的两道二分枚举最大流答案的问法很相识,但是这里用最大流速度 ...

  8. Swiper 滑动

    1.http://www.swiper.com.cn/download/  下载Swiper.JS  Swiper.CSS 2.引入项目,添加html <div class="cont ...

  9. Spring mvc 转发、重定向

    spring控制器最后返回一个ModelAndView(urlName),其中urNamel可以是一个视图名称,由视图解析器负责解析后将响应流写回客户端;也可以通过redirect/forward:u ...

  10. 团队作业4---第一次项目冲刺(ALpha)版本 第六天

    一.Daily Scrum Meeting照片 二.燃尽图 三.项目进展 a.完成所有基础功能 b.正在进行测试调试 四.困难与问题 1.测试前没有理清业务逻辑,导致前期测试深度不够: 2.在验证过去 ...