Ibatis的类型处理器TypeHandler解析

Ibatis允许用户像在hibernate中一样定义自己的类型,但是,用户自定义类型需要与数据库中的字段类型进行对应。它的处理方法是允许我们扩展TypeHandler。Ibatis框架在处理该数据类型时就会自动调用TypeHandler进行类型转换,非常方便,ibatis中所有的类型都有它自己的TypeHandler,只是一些常用的数据类类型它已经给我们实现了而已。

在配置文件中,我们有两个地方可以配置这种处理器。

  第一个地方是sqlMap文件中标签ResultMap或者ParameterMap中的TypeHandler属性,这里配置的handler是局部属性,只会在该ResultMap中才会进行转换。

  1.     <resultMap id="UserOrder" class="UserOrderDO" groupBy="id">
  2.          ......
  3.         <result property="tripType" column="trip_Type"
  4.          typeHandler="com.taobao.et.biz.dal.common.EnumTypeHandlerCallBack"/>
  5.           ......
  6.     </resultMap>

 第二个地方是sqlMapConfig文件中的标签typeHandlers中配置typeHandle子标签,这里配置的标签是全局属性,任何只要匹配该子标签的地方都会自动使用该Handler.

  1. <typeHandlers>
  2. <typeHandler jdbcType="CLOB" javaType="java.lang.String" callback="org.springframework.orm.ibatis.support.ClobStringTypeHandler"/>
  3. </typeHandlers>

例如这里的全局配置,如果此时某个数据库字段的jdbcType是CLOB类型,并且映射的JavaType类型是字符串类型,那么就会自动调用这里的callback来实行类型转换。

 

    那么Ibatis是如何确定使用哪一个TypeHandler的呢?!

    它会在自己的局部区域寻找是否在配置文件中配置了Handler,找到了就使用这个handler,如果没有找到,就会查找是否有全局handler,也就是第二种方式配置的handler,这里要注意,可能我们也没有在全局配置文件中配置handler,此时,Ibatis就会根据实际类型配置默认的 handler。

  我们来看一些关键代码,按照查找步骤,这里我去掉了异常,只看关键的部分。

  第一步:从局部Reultmap中取出配置属性。

  1.       String propertyName = childAttributes.getProperty("property");
  2.         String nullValue = childAttributes.getProperty("nullValue");
  3.         String jdbcType = childAttributes.getProperty("jdbcType");
  4.         String javaType = childAttributes.getProperty("javaType");
  5.         String columnName = childAttributes.getProperty("column");
  6.         String columnIndex = childAttributes.getProperty("columnIndex");
  7.         String statementName = childAttributes.getProperty("select");
  8.         String resultMapName = childAttributes.getProperty("resultMap");
  9.         String callback = childAttributes.getProperty("typeHandler");
  10.         callback = vars.typeHandlerFactory.resolveAlias(callback);
  11.         javaType = vars.typeHandlerFactory.resolveAlias(javaType);
  12.         TypeHandler handler = null;
  13.         if (callback != null) { // 注意这里,如果配置了就使用这个配置的handler
  14.             Object impl = Resources.classForName(callback).newInstance();
  15.             if (impl instanceof TypeHandlerCallback) {
  16.               handler = new CustomTypeHandler((TypeHandlerCallback) impl);
  17.             } else if (impl instanceof TypeHandler) {
  18.               handler = (TypeHandler) impl;
  19.             } else {
  20.               throw new NestedRuntimeException ("The class '' is not a valid implementation of TypeHandler or TypeHandlerCallback");
  21.             }
  22.         } else {//如果没有配置,就到这里来找
  23.                   handler = resolveTypeHandler(vars.client.getDelegate().getTypeHandlerFactory(), vars.currentResultMap.getResultClass(), propertyName, javaType, jdbcType, true);
  24.         }

 

第二步,如果局部配置中没有找到,就到下面去找。

 

  1. public TypeHandler resolveTypeHandler(TypeHandlerFactory typeHandlerFactory, Class clazz, String propertyName, String javaType, String jdbcType, boolean useSetterToResolve) {
  2.     TypeHandler handler = null;
  3.     if (clazz == null) {
  4.       // Unknown
  5.       handler = typeHandlerFactory.getUnkownTypeHandler();
  6.     } else if (DomTypeMarker.class.isAssignableFrom(clazz)) {
  7.       // DOM
  8.       handler = typeHandlerFactory.getTypeHandler(String.class, jdbcType);
  9.     } else if (java.util.Map.class.isAssignableFrom(clazz)) {
  10.       // Map
  11.       if (javaType == null) {
  12.         handler = typeHandlerFactory.getUnkownTypeHandler(); //BUG 1012591 - typeHandlerFactory.getTypeHandler(java.lang.Object.class, jdbcType);
  13.       } else {
  14.         try {
  15.           Class javaClass = Resources.classForName(javaType);
  16.           handler = typeHandlerFactory.getTypeHandler(javaClass, jdbcType);
  17.         } catch (Exception e) {
  18.           throw new NestedRuntimeException("Error.  Could not set TypeHandler.  Cause: " + e, e);
  19.         }
  20.       }
  21.     } else if (typeHandlerFactory.getTypeHandler(clazz, jdbcType) != null) {
  22.       // Primitive
  23.       handler = typeHandlerFactory.getTypeHandler(clazz, jdbcType);
  24.     } else {
  25.       // JavaBean
  26.       if (javaType == null) {
  27.         if (useSetterToResolve) {
  28.           Class type = PROBE.getPropertyTypeForSetter(clazz, propertyName);
  29.           handler = typeHandlerFactory.getTypeHandler(type, jdbcType);
  30.         } else {
  31.           Class type = PROBE.getPropertyTypeForGetter(clazz, propertyName);
  32.           handler = typeHandlerFactory.getTypeHandler(type, jdbcType);
  33.         }
  34.       } else {
  35.         try {
  36.           Class javaClass = Resources.classForName(javaType);
  37.           handler = typeHandlerFactory.getTypeHandler(javaClass, jdbcType);
  38.         } catch (Exception e) {
  39.           throw new NestedRuntimeException("Error.  Could not set TypeHandler.  Cause: " + e, e);
  40.         }
  41.       }
  42.     }
  43.     return handler;
  44.   }
  45. 相信大家已经很明白了,其实要找一个Handler,主要就是需要javaType和jdbcType,而这两个参数要么通过反射得到,要么通过配置文件中得到。因此,为了明确我们一般都在配置文件中进行申明。
  46. 最后来看一点 typeHandlerFactory.getTypeHandler(clazz, jdbcType)是怎么实现的。
    1.   public TypeHandler getTypeHandler(Class type, String jdbcType) {
    2.     Map jdbcHandlerMap = (Map) typeHandlerMap.get(type);//首先根据JAVA类型
    3.     TypeHandler handler = null;
    4.     if (jdbcHandlerMap != null) {
    5.       handler = (TypeHandler) jdbcHandlerMap.get(jdbcType);//每个JDBC类型
    6.       if (handler == null) {
    7.         handler = (TypeHandler) jdbcHandlerMap.get(null);
    8.       }
    9.     }
    10.     return handler;
    11.   }
  47. 其实一个Handler=javaType+jdbcType 。

 

转化类,实现TypeHandler接口,实现了接口的三个方法

public class GenderEnumTypeHandler implements TypeHandler
{

 @Override//查询时对数据的转化
 public Object getResult(ResultSet rs, String str) throws SQLException
 {
  int v = rs.getInt(str);
  return GenderEnum.getGender(v);
 }

 @Override
 public Object getResult(CallableStatement arg0, int arg1)
   throws SQLException
 {
  // TODO Auto-generated method stub
  return null;
 }

 @Override//插入时对数据的转化
 public void setParameter(PreparedStatement prmt, int index, Object arg2,
   JdbcType arg3) throws SQLException
 {
  GenderEnum gender = (GenderEnum)arg2;
  prmt.setInt(index, gender.value);
 }

}

 

 

定义的枚举

package common;

public enum GenderEnum
{
 MAN(0),
 WOMAN(1);
 
 public final int value;
 
 public int getValue()
 {
  return value;
 }
 private GenderEnum(int value) {
  this.value = value;
 }
 

 public static GenderEnum getGender(int v)
 {
  GenderEnum gender = null;
  GenderEnum[] list = GenderEnum.values();
  
  for(GenderEnum g : list)
  {
   if(g.value == v)
   {
    gender = g;
   }
  }
  return gender;
 }
}

 

配置文件(/ibatis/src/Student.xml)

 <insert id="add" parameterType="studentBean">
  insert into t_student(fid, fname, fbirthday, fgender) values
   newbosid('SFSAFSDF'),
    #{name, jdbcType=VARCHAR},
    #{birthday},
    #{gender.value}
    <!--  #{gender typeHandler=common.GenderEnumTypeHandler javaType=common.GenderEnum
jdbcType=INTEGER}-->
   )
 </insert>

 

 <select id="getAll" resultType="studentBean" resultMap="studentResultMap">
  select stu.fid fid,
   stu.fname fname,
   dept.fid deptid,
   dept.fname deptname,
   stu.fgender,
   stu.fbirthday
  from t_student stu
   inner join t_afei_dept dept on dept.fid = stu.fdept
 </select>

 

 <!-- 数据库字段和Bean对象字段映射 -->
 <resultMap type="studentBean" id="studentResultMap">
  <id property="id" column="fid"/>
  <result property="name" column="fname"/>
  <result property="birthday" column="fbirthday"/>
  <result property="gender" column="fgender" typeHandler="common.GenderEnumTypeHandler"
   javaType="common.GenderEnum" jdbcType="INTEGER"/>
  
  <!-- 联合查询 property中的名字应该和deptBean这个名字一致
   column中的名字应该和查询时的别名一样 -->
  <association property="dept" column="fdept" javaType="deptBean">
   <id property="id" column="deptid"/>
   <result property="name" column="deptname"/>
   <result property="num" column="deptnum"/>
  </association>
 </resultMap>

红色部分为增加的枚举

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

method_chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值