一:TypeHandler的定義
mybatis是orm(對象關系模型)框架,需要實現pojo與數據庫jdbcType的轉換,當設置參數時,會調用到DefaultParameterHandler的setParameter方法,根據參數尋找不同的TypeHandler,將參數設置到PreparedStatement語句中。當返回結果時,會調用DefaultResultSetHandler的getPropertyMappingValue方法,根據propertyMapping找到TypeHandler,
調用TypeHandler的getResult方法,從ResultSet中讀取數據,然后封裝到pojo上
二:自定義TypeHandler的使用
1: 定義一個幣種的枚舉
public enum Currency {
cny("CNY","人民幣"),
usd("USD","美元");
private String code;
private String desc;
Currency(String code,String desc){
this.code = code;
this.desc = desc;
}
private static Map<String,Currency> map = new ConcurrentHashMap<>();
static{
for(Currency currency:values()){
map.put(currency.code,currency);
}
}
public static Currency getCurrencyByCode(String code){
return map.get(code);
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
定義一個關於幣種轉換的自定義TypeHandler,設置參數時,將枚舉的幣種值轉換為字符串,設置到statement中,返回
結果時,將字符串的值轉換為枚舉值
public class CurrencyTypeHandler extends BaseTypeHandler<Currency> {
@Override public void setNonNullParameter(PreparedStatement ps, int i, Currency parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i,parameter.getCode());
}
@Override public Currency getNullableResult(ResultSet rs, String columnName) throws SQLException {
String currency = rs.getString(columnName);
return Currency.getCurrencyByCode(currency);
}
@Override public Currency getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}
@Override public Currency getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}
mybatis-config.xml中的配置:
<typeHandlers>
<typeHandler javaType="com.example.mybatis.model.Currency" jdbcType="VARCHAR"
handler="com.example.mybatis.model.CurrencyTypeHandler"/>
</typeHandlers>
mapper.xml中resultMap元素直接配置result子元素:
<mapper namespace="com.example.mybatis.mapper.UserMapper">
<resultMap id="baseResultMap" type="com.example.mybatis.model.User">
<result column="id" property="id" jdbcType="INTEGER"></result>
<result column="name" property="name" jdbcType="VARCHAR"></result>
<result column="age" property="age" jdbcType="VARCHAR"></result>
<result column="currency" property="currency" jdbcType="VARCHAR"
typeHandler="com.example.mybatis.model.CurrencyTypeHandler"></result>
</resultMap>
<resultMap id="test" type="com.example.mybatis.model.User"/>
<insert id="insert" parameterType="com.example.mybatis.model.User">
insert into user (name,age) value(#{name},#{age})
</insert>
<select id="listUsers" resultMap="baseResultMap" parameterType="com.example.mybatis.model.User">
select * from user where name = #{name} and currency = #{currency,javaType=com.example.mybatis.model.Currency,
jdbcType=VARCHAR,typeHandler=com.example.mybatis.model.CurrencyTypeHandler}
</select>
</mapper>
測試代碼:
public static void main(String[] args) throws IOException {
// 將mybatis-config的配置文件讀入內存,生成字符流對象
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 解析全局配置文件mybatis-config.xml
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// PageHelper.startPage(1,2);
// 測試一級緩存:
List<User> list = userMapper.listUsers("hello105", Currency.USD);
System.out.println("第一次查詢結果:" + list.size());
System.out.println("第一次查詢結果:" + list);
}
運行結果:字符串已經轉換為枚舉值:

1:在mybatis-config.xml中配置 typeHandlers元素
2:在mapper.xml中配置result子元素,配置typeHandler屬性,在#{currency}中配置currency的jdbcType、javaType以及typeHandler屬性
或者不在mybatis-config.xml中配置typeHandler元素,直接在mapper.xml中的result子元素上配置typeHandler屬性
三:從源碼層面分析一下如何實現的
1:注冊typeHandler

解析typeHandler元素,然后調用register方法注冊

將解析后的javaType以及jdbcType ,handler緩存到map中

設置參數的時候,調用preparedStatement 的setParameters方法:

如果parameter是Currency類型,那么獲取TypeHandler,調用typeHandler的setParameter方法:

調用到自定義的CurrencyTypeHandler類,將枚舉值Currency獲取String值,set到preparedStatement中:

返回值的時候,從resulstSet中取出字符串值,需要轉換成Currency類型



從ResultSet中獲取結果:

從resultSet中讀取到字符串的值,然后根據字符串轉為Currency類型對象

總結:
mybatis-config.xml中配置的typeHandlers元素可以不用配置,不配置就使用懶加載的方式,在解析sql時創建typeHandler對象,然后實現注冊。
1:參數設置時的轉換,需要在參數上配置屬性#{jdbcType= javaType= typeHandler=},在設置參數使,ps會根據參數屬性找到typeHandler,然后轉換
2:獲取結果時,需要在resultMap上面配置result子元素,在獲取結果時,會根據參數屬性找到typeHandler,然后拿到resultSet中的原始值,然后在自定義的
TypeHandler中實現轉換。
