建立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. Android-Error3:Error when loading the SDK

    解决方法: 用C:\android\sdk\tools中的devices.xml将出现错误的地方的devices.xml替换掉既可以了.

  2. [js高手之路]设计模式系列课程-设计一个模块化扩展功能(define)和使用(use)库

    模块化的诞生标志着javascript开发进入工业时代,近几年随着es6, require js( sea js ), node js崛起,特别是es6和node js自带模块加载功能,给大型程序开发 ...

  3. git 工作流介绍

    GIT Git工作流你可以理解为工作中团队成员遵守的一种代码管理方案,在Git中有以下几种工作流方案作为方案指导: 集中式工作流 功能开发工作流 Gitflow工作流 Forking工作流 下面针对性 ...

  4. 认清Javascript的地位并编写合理的Javascript代码

    作为前端程序员,一定要认清javascript的地位,不要被它乱七八糟的特点所迷惑.JavaScript主要是用来操控和重新调整DOM,通过修改DOM结构,从而达到修改页面效果的目的. 要用这个中心思 ...

  5. 编译安装dropbear

    author:JevonWei 版权声明:原创作品 dropbear也可实现ssh远程登录的作业 1. 安装开发包组 yum -y groupinstall "Development Too ...

  6. hdu 6199 沈阳网络赛---gems gems gems(DP)

    题目链接 Problem Description Now there are n gems, each of which has its own value. Alice and Bob play a ...

  7. 一,ESP8266下载和刷固件

    用自己的小板测试...... 安排上呢 一, ESP8266下载和刷固件(Lua开发----体验一下lua开发的魅力所在) 二, 控制一个灯亮灭 三, TCP服务器 四, TCP客户端 五, UDP ...

  8. Bash 脚本进阶,经典用法及其案例

    前言:在linux中,Bash脚本是很基础的知识,大家可能一听脚本感觉很高大上,像小编当初刚开始学一样,感觉会写脚本的都是大神.虽然复杂的脚本是很烧脑,但是,当我们熟练的掌握了其中的用法与技巧,再多加 ...

  9. grunt对象之api

    grunt已经扯了七篇了,殊为不易.最后一篇扯点早应该提及的东西,就是module.exports = function(grunt) {}传入的这个grunt.之前的代码grunt一般只出现在Gru ...

  10. MySQL (九)-- 代码执行结构、函数、存储过程

    1 代码执行结构 代码执行结构有三种:顺序结构.分支结构和循环结构. 1.1 分支结构 分支结构:实现准备多个代码块,按照条件选择性执行某段代码. 在MySQL中只有if分支. 基本语法 if 条件判 ...