1. 程式人生 > >mybatis精通之路之TypeHandler初探

mybatis精通之路之TypeHandler初探

前言:直入正題,在我們利用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:[email protected]
 * <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進行檢視。