浅析 mybatis 中 TypeHandler 类型转换器 + 自定义 TypeHandler

阅读 1791
收藏 13
2017-05-22
原文链接:blog.csdn.net

前言:直入正题,在我们利用mybatis作为持久层框架存储数据时,从mybatis接收参数到MySQL存储数据,都会用到typeHandler类型处理器。这也就是从JavaType->JdbcType的转化过程。由于mybatis初始时已经内置大部分基础类型转化的TypeHandler,已经足够我们平常的简单应用开发了,所以大多数情况下并不需要我们自己去定义类型转换器。但是,当遇到一些特殊情况时,为了开发的方便性,我们才回去自定义一些类型转换器。

使用场景:mybatis在预处理语句(PreparedStatement)中设置一个参数时,或者从结果集(ResultSet)中取出一个值时,都会用到TypeHandler。它的作用就是将Java类型(javaType)转化为jdbc类型(jdbcType),或者将jdbc类型(jdbcType)转化为java类型(javaType)。

mybatis初始内置TypeHandler:

 public TypeHandlerRegistry() {
        this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));
        this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));
        this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));
        this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));
        this.register((Class)Byte.class, (TypeHandler)(new ByteTypeHandler()));
        this.register((Class)Byte.TYPE, (TypeHandler)(new ByteTypeHandler()));
        this.register((JdbcType)JdbcType.TINYINT, (TypeHandler)(new ByteTypeHandler()));
        this.register((Class)Short.class, (TypeHandler)(new ShortTypeHandler()));
        this.register((Class)Short.TYPE, (TypeHandler)(new ShortTypeHandler()));
        this.register((JdbcType)JdbcType.SMALLINT, (TypeHandler)(new ShortTypeHandler()));
        this.register((Class)Integer.class, (TypeHandler)(new IntegerTypeHandler()));
        this.register((Class)Integer.TYPE, (TypeHandler)(new IntegerTypeHandler()));
        this.register((JdbcType)JdbcType.INTEGER, (TypeHandler)(new IntegerTypeHandler()));
        this.register((Class)Long.class, (TypeHandler)(new LongTypeHandler()));
        this.register((Class)Long.TYPE, (TypeHandler)(new LongTypeHandler()));
        this.register((Class)Float.class, (TypeHandler)(new FloatTypeHandler()));
        this.register((Class)Float.TYPE, (TypeHandler)(new FloatTypeHandler()));
        this.register((JdbcType)JdbcType.FLOAT, (TypeHandler)(new FloatTypeHandler()));
        this.register((Class)Double.class, (TypeHandler)(new DoubleTypeHandler()));
        this.register((Class)Double.TYPE, (TypeHandler)(new DoubleTypeHandler()));
        this.register((JdbcType)JdbcType.DOUBLE, (TypeHandler)(new DoubleTypeHandler()));
        this.register((Class)Reader.class, (TypeHandler)(new ClobReaderTypeHandler()));
        this.register((Class)String.class, (TypeHandler)(new StringTypeHandler()));
        this.register((Class)String.class, JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
        this.register((Class)String.class, JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
        this.register((Class)String.class, JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
        this.register((Class)String.class, JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
        this.register((Class)String.class, JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
        this.register((Class)String.class, JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
        this.register((Class)String.class, JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
        this.register((JdbcType)JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
        this.register((JdbcType)JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
        this.register((JdbcType)JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
        this.register((JdbcType)JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
        this.register((JdbcType)JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
        this.register((JdbcType)JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
        this.register((JdbcType)JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
        this.register((Class)Object.class, JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
        this.register((JdbcType)JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
        this.register((Class)BigInteger.class, (TypeHandler)(new BigIntegerTypeHandler()));
        this.register((JdbcType)JdbcType.BIGINT, (TypeHandler)(new LongTypeHandler()));
        this.register((Class)BigDecimal.class, (TypeHandler)(new BigDecimalTypeHandler()));
        this.register((JdbcType)JdbcType.REAL, (TypeHandler)(new BigDecimalTypeHandler()));
        this.register((JdbcType)JdbcType.DECIMAL, (TypeHandler)(new BigDecimalTypeHandler()));
        this.register((JdbcType)JdbcType.NUMERIC, (TypeHandler)(new BigDecimalTypeHandler()));
        this.register((Class)InputStream.class, (TypeHandler)(new BlobInputStreamTypeHandler()));
        this.register((Class)Byte[].class, (TypeHandler)(new ByteObjectArrayTypeHandler()));
        this.register((Class)Byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
        this.register((Class)Byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
        this.register((Class)byte[].class, (TypeHandler)(new ByteArrayTypeHandler()));
        this.register((Class)byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
        this.register((Class)byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
        this.register((JdbcType)JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
        this.register((JdbcType)JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
        this.register(Object.class, this.UNKNOWN_TYPE_HANDLER);
        this.register(Object.class, JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
        this.register(JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
        this.register((Class)Date.class, (TypeHandler)(new DateTypeHandler()));
        this.register((Class)Date.class, JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
        this.register((Class)Date.class, JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
        this.register((JdbcType)JdbcType.TIMESTAMP, (TypeHandler)(new DateTypeHandler()));
        this.register((JdbcType)JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
        this.register((JdbcType)JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
        this.register((Class)java.sql.Date.class, (TypeHandler)(new SqlDateTypeHandler()));
        this.register((Class)Time.class, (TypeHandler)(new SqlTimeTypeHandler()));
        this.register((Class)Timestamp.class, (TypeHandler)(new SqlTimestampTypeHandler()));

上面都是mybatis内置的处理器,所以在平时开发的时候我们不用去关心java到数据库的类型转化关系,mybatis都帮我们把这些工作完成了。但这并不是我们要关心的重点,我们需要的是自定义TypeHandler去应对更多的需求。

对TypeHandler接口的一些说明:

TypeHandler是一个接口,它定义了如下四个方法,实现类必须去实现,方法如下:

 void setParameter(PreparedStatement var1, int var2, T var3,     JdbcType var4) throws SQLException;

    T getResult(ResultSet var1, String var2) throws SQLException;

    T getResult(ResultSet var1, int var2) throws SQLException;

    T getResult(CallableStatement var1, int var2) throws SQLException;
}

setParameter:通过preparedStatement对象设置参数,将T类型的数据存入数据库。

getResult:通过列名或者下标来获取结果数据,也可以通过CallableStatement获取数据。

自定义注解里还可以通过注解配置java类型和jdbc类型:
@MappedTypes() :注解配置java类型
@MappedJdbcTypes(): 注解配置jdbc类型

枚举类型的TypeHandler:
EnumOrdinalTypeHandler:使用枚举字符串名称作为参数传递。
EnumTypeHandler:使用整数下标作为参数传递。

为了实现自定义枚举类型的TypeHandler,我们通过性别(Sex)类型来进行演示:

首先定义Sex类型的枚举,定义如下:

package com.cbg.Entity;

/**
 * Created by chenboge on 2017/5/18.
 * <p>
 * Email:baigegechen@gmail.com
 * <p>
 * description:
 */
//用于SexTypeHandler的性别转换器枚举
public enum Sex {
//每一个类型都是一个枚举类(Sex)的实例
    MALE(0, "男"),
    FMALE(1, "女");

    //用于保存在数据库
    private int SexCode;
    //用于UI展示
    private String SexName;

    Sex(int sexCode, String sexName) {
        SexCode = sexCode;
        SexName = sexName;
    }

    public int getSexCode() {
        return SexCode;
    }

//通过SexCode的值来获取Sex枚举类型,数据库只需保存code,通过代码解析成Sex类型
    public static Sex getSexFromCode(int code) {
        for (Sex sex : Sex.values()) {
            if (sex.getSexCode() == code) {
                return sex;
            }
        }
        return null;
    }
}

如果我们不自定义TypeHandler,也可以通过mybatis内置的EnumOrdinalTypeHandler来进行类型转换,在mapper文件中的resultmap中进行如下配置:

<result property="sex" column="sex" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>

自定义枚举TypeHandler:

package com.cbg.handler;

import com.cbg.Entity.Sex;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Created by chenboge on 2017/5/18.
 * <p>
 * Email:baigegechen@gmail.com
 * <p>
 * description:
 */
public class SexTypeHandler implements TypeHandler<Sex> {
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, Sex sex, JdbcType jdbcType) throws SQLException {
        //设置第i个参数的值为传入sex的code值,preparedStatement为执行数据库操纵的对象
        //传值的时候是一个sex对象,但是当进行映射插入的时候就会转化为sex的code值进行存储
        preparedStatement.setInt(i, sex.getSexCode());
    }

    @Override
    public Sex getResult(ResultSet resultSet, String s) throws SQLException {
        //获取数据库存储的sex的code值
        int result = resultSet.getInt(s);
        return Sex.getSexFromCode(result);
    }

    @Override
    public Sex getResult(ResultSet resultSet, int i) throws SQLException {
        int result = resultSet.getInt(i);
        return Sex.getSexFromCode(result);
    }

    @Override
    public Sex getResult(CallableStatement callableStatement, int i) throws SQLException {
        int result = callableStatement.getInt(i);
        return Sex.getSexFromCode(result);
    }
}

最后需要在配置文件中运用我们自定义的TypeHandler对Sex进行类型转换:

   <resultMap id="userMap" type="com.cbg.pojo.UserBean">
        <result property="id" column="id"/>
        <result property="userName" column="user_name"/>
        <result property="birthday" column="birthday"/>
        <result property="mobile" column="mobile"/>
        <result property="email" column="email"/>
        <result property="note" column="note"/>
        <result property="sex" column="sex" javaType="com.cbg.Entity.Sex" jdbcType="INTEGER"
                typeHandler="com.cbg.handler.SexTypeHandler"/>
    </resultMap>

通过上面的配置,自定义的TypeHandler就会生效,对java中的Sex对象和数据库中的Integer进行自定转换。数据库只需存储Sex的code值,获取数据库数据时自动转化为Sex枚举对象,使用相当方便,也更加灵活。

总结:自定义TypeHandler总体上来说还是比较简单的。还有不清楚的同学可以移步:ssm基础框架整合+自定义枚举TypeHandler进行查看。

评论