myBatis中的语句映射几种特别方式

来源:互联网 发布:图像增强算法有哪些 编辑:程序博客网 时间:2024/05/17 16:03

对于myBatis中的高级映射通常会使用resultMap参数,而非使用resultType参数,虽然对于大多数的查询来说resultType会更常用,其实这两种方式都是比较好使用的,只通常来说,当配置为resultType参数,所做的映射查询通常会简单一些,而使用resultMap参数要相对复杂一些,不如果不是太复杂使用resultMap没有太大的意义,因为它需要多配置一个resutMap映射结点对象,但有时你会发现,在查询时90%字段来源于一个表,而只有相别字段来源于另一个表,此时如果使用resultMap其实就有些得不偿失,但有些开发人却不知道怎样使用较简单的方法来处理它,其实很很简单,具体可以查看下面的配置:

<select id="getLogs" parameterType="CoreLog" resultType="CoreLog">select<include refid="fieldAsProperty" />,bb.cract_Name AS "strMap.cractName"from core_log aa left join core_account bb on aa.crlog_cract_uuid = bb.cract_uuid<where><include refid="conditionA" /></where>order by crlog_cdate desc</select>

上面其实就是使用比较简单的方法将数据库列映射到了bean的一个属性strMap.cractName上面,这是一个带有链式的语法,如果采用resultMap写法可能就会比较麻烦,例如: 

<resultMap type="CoreLog" id="fieldMapperEX" extends="fieldMapper"><result property="strMap.cractName" column="cractName"/></resultMap><select id="getLogs" parameterType="CoreLog" resultMap="fieldMapperEX">select<include refid="fieldAsProperty" />,bb.cract_Name AS cractNamefrom core_log aa left join core_account bb on aa.crlog_cract_uuid = bb.cract_uuid<where><include refid="conditionA" /></where>order by crlog_cdate desc</select>

这里需要修正说明一下:对于上面的第一种写法是不完全正确的,这可能是当前myBatis3.0.5不够完善的地方这里需要说明下这两种写法的原理,其实终这两种写法最终的落脚点在第二种方法,第一种方法只所不需要这种配置是因为它通过指定resultType与列名称进行了一种反射不区分大小写的适配,但这是种适配现在还有不完善的地方,主要体现在链式语法中间含有一些非javaBean类型(例如上面的Map类型),则框架进行推测就会出错,具体逻辑可以查看以下源代码.

  protected boolean applyAutomaticMappings(ResultSet rs, List<String> unmappedColumnNames, MetaObject metaObject) throws SQLException {    boolean foundValues = false;    for (String columnName : unmappedColumnNames) {      final String property = metaObject.findProperty(columnName);      if (property != null) {        final Class propertyType = metaObject.getSetterType(property);        if (typeHandlerRegistry.hasTypeHandler(propertyType)) {          final TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propertyType);          final Object value = typeHandler.getResult(rs, columnName);          if (value != null) {            metaObject.setValue(property, value);            foundValues = true;          }        }      }    }    return foundValues;  }
  public String findProperty(String propName) {    return objectWrapper.findProperty(propName);  }
  public String findProperty(String name) {    return metaClass.findProperty(name);  }
  public String findProperty(String name) {    StringBuilder prop = buildProperty(name, new StringBuilder());    return prop.length() > 0 ? prop.toString() : null;  }
  private StringBuilder buildProperty(String name, StringBuilder builder) {    PropertyTokenizer prop = new PropertyTokenizer(name);    if (prop.hasNext()) {      String propertyName = reflector.findPropertyName(prop.getName());      if (propertyName != null) {        builder.append(propertyName);        builder.append(".");        MetaClass metaProp = metaClassForProperty(propertyName);        metaProp.buildProperty(prop.getChildren(), builder);      }    } else {      String propertyName = reflector.findPropertyName(name);      if (propertyName != null) {        builder.append(propertyName);      }    }    return builder;  }
public String findPropertyName(String name) {return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH));}

通过源代码的追踪你会发现缺陷出在org.apache.ibatis.reflection.Reflector#findPropertyName,它只考虑了标准的javaBean反射,对于Map类型的数据没有进行考虑,当然它是指在链式语法的中间存在Map型数据才会不能正确的返回属性的名称,或或许在将来的某个版本中可以支持类似Map类型数据的链式语法. 

但是对于javaBean成员字段属性则不存在这样的问题,例如:

<select id="getMultiOrgsByUsr" resultType="CoreAccount" parameterType="CoreAccount">select cc.cract_name as cractName, cc.cract_Unid as cractUnid,cc.cract_Uuid as cractUuid,cc.cract_crorg_uuid as cractCrorgUuid,aa.cramo_unid as "coreAccountOrganiz.cramoUnid",bb.crorg_full_name as "coreOrganization.crorgFullName",bb.crorg_uuid as "coreOrganization.crorgUuid"from core_account_organiz aa left join core_organization bb on aa.cramo_crorg_uuid = bb.crorg_uuidleft join core_account cc on aa.cramo_cract_uuid = cc.cract_uuidwhere aa.cramo_cract_uuid = #{cractUuid}</select>

上面的coreAccountOrganiz与coreOrganization都属于CoreAccount的成员字段,将其中的几个数据库列字段映射到成员中的某几个字段段上,从上面的分析可以看出对从一张表查询所有字段,而从另外的一张表中查询少量字段可以优先使用resultType参数是一种最适合的方法,对于resultMap配置,通常对于单表映射开发人员都会使用代码生成工具生成,对于这种情况,如果希望映射出类似于Hibernate的效果,如果还使用resultType参数就会有些不好做,需要写很多SQL,如果使用resultMap参数就可以共享单表的映射定义,具体参考下面的配置:

这上CoreAccount的映射配置:

<resultMap id="fieldMapper" type="CoreAccount"><id property="cractUnid" column="cractUnid" /><!--标识UNID--><result property="cractUuid" column="cractUuid" /><!--标识UUID--><result property="cractLevelCode" column="cractLevelCode" /><!--层级编码--><result property="cractCrorgUuid" column="cractCrorgUuid" /><!--默认机构--><result property="cractIdNumber" column="cractIdNumber" /><!--身份证号--><result property="cractMobile" column="cractMobile" /><!--手机号码--><result property="cractJobNumber" column="cractJobNumber" /><!--职位代码--><result property="cractLevel" column="cractLevel" /><!--帐户级别--><result property="xxxxx" column="xxxx" /><!--帐户代码--></resultMap>

下面是CoreAccountOrganiz映射配置:

<resultMap id="fieldMapper" type="CoreAccountOrganiz"><id property="cramoUnid" column="cramoUnid" /><!--标识UNID--><result property="cramoCractUuid" column="cramoCractUuid" /><!--帐户UUID--><result property="cramoCrorgUuid" column="cramoCrorgUuid" /><!--机构部门UUID--><result property="cramoOrd" column="cramoOrd" /><!--排序号--><result property="cramoExtCrorgUuid" column="cramoExtCrorgUuid" /><!--机构UUID--><result property="cramoExt1" column="cramoExt1" /><!--扩展字段1--><result property="cramoExt2" column="cramoExt2" /><!--扩展字段2--></resultMap>

如果这两张表要进行关联查询映射,并且希望在CoreAccount对象中获取一个完整的CoreAccountOrganiz成员对象,则可以使用下面的方式:

<resultMap type="CoreAccount" id="fieldMapperEX" extends="fieldMapper"><association property="coreOrganization" resultMap="com.risen.core.dao.mapper.ICoreOrganizationMapper.fieldMapper" /><association property="coreAccountOrganiz" resultMap="com.risen.core.dao.mapper.ICoreAccountOrganizMapper.fieldMapper" /></resultMap>

这上面是映射两个成员对象,上面只是列举了一个,此时你就可以使用

<select id="getAccountByOrg" parameterType="CoreAccount" resultMap="fieldMapperEX">select <include refid="fieldAsProperty" />,<include refid="com.risen.core.dao.mapper.ICoreOrganizationMapper.fieldAsProperty" />,<include refid="com.risen.core.dao.mapper.ICoreAccountOrganizMapper.fieldAsProperty" />from core_account_organiz aajoin core_account bb on aa.cramo_cract_uuid = bb.cract_uuidjoin core_organization cc on bb.cract_crorg_uuid = cc.crorg_uuidwhere aa.cramo_crorg_uuid = #{cractCrorgUuid} and bb.cract_status != 2<if test="cractLevel != null" >and cract_level=#{cractLevel}</if>order by aa.cramo_ord</select>

来进行映射查询,而这里使用的是resultMap查询,此如果你需要映射字段就不应该在语句中来完成,而应该在fieldMapperEX对象中去定义,例如先前使用bb.cract_Name AS "strMap.cractName"这种语法基本是不起作用的因为在fieldMapperEX对象并没有定义strMap.cractName这样的字段映射,如果此时你还希望进行个别字段的映射还是需要在fieldMapperEX对象定义:strMap.cractName的映射,例如:

<resultMap type="CoreAccount" id="fieldMapperEX" extends="fieldMapper">
<result property="strMap.cractName" column="xxxxxxx" /><association property="coreOrganization" resultMap="com.risen.core.dao.mapper.ICoreOrganizationMapper.fieldMapper" /><association property="coreAccountOrganiz" resultMap="com.risen.core.dao.mapper.ICoreAccountOrganizMapper.fieldMapper" /></resultMap>

其实从这里对比着看你会发现result标签与association标签之间的其实差不多,只是association定义属类似于定义了一个前缀而已,上面也可以将strMap对象映射到另一个resultMap对象而在另一个resultMap对象不去映射字段:

<resultMap type="CoreLog" id="fieldMapperEX" extends="fieldMapper"><result property="cractName" column="cractName"/></resultMap>


此时你的strMap前缀就可以被取消掉了