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進行檢視。