最新公告
  • 新注册用户请前往个人中心绑定邮箱以便接收相关凭证邮件!!!点击前往个人中心
  • Mybatis 处理列名—字段名映射(一) :驼峰式命名映射

    在之前的博客-《 [JDBC] 处理ResultSet,构建Java对象 》中提到,我们需要分析Mybatis在转换Result到需要的Java业务对象时做的三件事,如下:

     [JDBC] 处理ResultSet,构建Java对象
    https://my.oschina.net/kailuncen/blog/906992

    1. 解决了数据库列名到Java列名的映射。
    2. 解决了数据库类型到Java类型的转换工作。
    3. 在转换过程中具备一定的容错能力。

    其实核心就是:

    1. 数据库中的列名怎么和对象中的字段对应起来。
    2. 数据库中的列的类型怎么转换到合适的Java类型,不引起转换失败。

    今天我们先来看第一点,数据库中的列名怎么和对象中的字段对应起来。首先是日常PO(Persistant Object) CityPO,里面有五个字段。

    public class CityPO {
        Integer id;
        Long cityId;
        String cityName;
        String cityEnName;
        String cityPyName;

    本次要查询的数据库中的列名如下所示。

    mysql> mysql> desc SU_City;
    +————–+————-+——+—–+——————-+—————————–+
    | Field        | Type        | Null | Key | Default           | Extra                       |
    +————–+————-+——+—–+——————-+—————————–+
    | id           | int(11)     | NO   | PRI | NULL              | auto_increment              |
    | city_id      | int(11)     | NO   | UNI | NULL              |                             |
    | city_name    | varchar(20) | NO   |     |                   |                             |
    | city_en_name | varchar(20) | NO   |     |                   |                             |
    | city_py_name | varchar(50) | NO   |     |                   |                             |
    | create_time  | datetime    | NO   |     | CURRENT_TIMESTAMP |                             |
    | updatetime   | datetime    | NO   | MUL | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
    +————–+————-+——+—–+——————-+—————————–+
    7 rows in set (0.01 sec)

    我们是按照驼峰式命名,把数据库中的列名对应到了对象的字段名。如下是Mybatis的接口类和映射文件。

    public interface CityMapper {
     
        CityPO selectCity(int id);
    }

    <?xml version=”1.0″ encoding=”UTF-8″ ?>
    <!DOCTYPE mapper
            PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN”
            “http://mybatis.org/dtd/mybatis-3-mapper.dtd”>
    <mapper namespace=”mapper.CityMapper”>
        <select id=”selectCity” resultType=”po.CityPO”>
            select id,city_id,city_name,city_en_name from SU_City where id = #{id}
        </select>
    </mapper>

    在上面的映射文件中,namespace指定了这个接口类的全限定类名,紧随其后的select代表是select语句,id是接口类中函数的名字,resultType代表了从这条语句中返回的期望类型的类的完全限定名或别名,在此例子中是我们的业务对象CityPO的类路径。

    主要有三种方案

    1. 驼峰式命名开关,或者不开,数据库列和字段名全一致。
    2. Select时指定AS。
    3. resultMap 最稳健。

    这篇主要看一下第一种,附上示例和部分源码走读。

    1.驼峰命名开关。

    因为CityPO的列名是完全根据数据库列名驼峰式命名后得到的,因此Mybatis提供了一个配置项。开启开配置项后,在匹配时,能够根据数据库列名找到对应对应的驼峰式命名后的字段。

    <settings>
        <!– 开启驼峰,开启后,只要数据库字段和对象属性名字母相同,无论中间加多少下划线都可以识别 –>
        <setting name=”mapUnderscoreToCamelCase” value=”true” />
    </settings>

    我们从源码角度解读一下,Mybat处理ResultSet的映射默认都在DefaultResultSetHandler中完成。

    处理行数据的时候的时候主要在下面的函数里进行,由于我们在映射文件中没有定义额外的ResultMap,因此会直接进入else分支的代码。

    public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        if (resultMap.hasNestedResultMaps()) {
          ensureNoRowBounds();
          checkResultHandler();
          handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {
          handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }
    }

    进入handleRowValuesForSimpleResultMap中,主要处理函数如下,在这里完成了对象的生成及赋值。

    Object rowValue = getRowValue(rsw, discriminatedResultMap);

    在这里先创建了对象的实例,然后获取了对象的元信息,为反射赋值做准备。

    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
        if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
          final MetaObject metaObject = configuration.newMetaObject(rowValue);
          boolean foundValues = this.useConstructorMappings;
          if (shouldApplyAutomaticMappings(resultMap, false)) {
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
          }
          foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
          foundValues = lazyLoader.size() > 0 || foundValues;
          rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
        }
        return rowValue;
      }

    在applyAutomaticMappings完成了整个过程,我们进去探一探。

    就是下面这个函数创建好了映射关系,这个函数的下半部分是完成赋值的,映射的部分下次会详细分析。

    List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);

    在这个方法里,上半部分是生成了数据库的列名,在这个函数中找到了对应的字段名。

    final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());

    我们进去看一看,它传进了生成好的数据库列名,传进了前面提到的是否根据驼峰式命名映射开关的值。

    事实证明,真的很简单,往下看,就是把下划线都去了。

    public String findProperty(String name, boolean useCamelCaseMapping) {
        if (useCamelCaseMapping) {
          name = name.replace(“_”, “”);
        }
        return findProperty(name);
      }

    隐隐觉得是不是大小写不敏感啊,继续往下看,这里返回找到的字段名。

    private StringBuilder buildProperty(String name, StringBuilder builder) {
         ……….
         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));
    }


    所以如果你数据库里字段是city_id,city_Id,大写I,那么可能会有问题吧,不过仔细想想,谁会吃力不讨好干这种事情,硬要处理成标准的驼峰式命名也可以啦,不过感觉必要性不大。

    经过若干次中途崩溃,我终于写完了驼峰式命名开关下,我们是如何完成数据库列和字段名的映射的。后面的博文会继续看看后续两种方案以及DDL时对象字段是如何赋值到Sql语句中。
    本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
    极客文库 » Mybatis 处理列名—字段名映射(一) :驼峰式命名映射

    常见问题FAQ

    如果资源链接失效了怎么办?
    本站用户分享的所有资源都有自动备份机制,如果资源链接失效,请联系本站客服QQ:2580505920更新资源地址。
    如果用户分享的资源与描述不符怎么办?
    可以联系客服QQ:2580505920,如果要求合理可以安排退款或者退赞助积分。
    如何分享个人资源获取赞助积分或其他奖励?
    本站用户可以分享自己的资源,但是必须保证资源没有侵权行为。点击个人中心,根据操作填写并上传即可。资源所获收益完全归属上传者,每周可申请提现一次。
    如果您发现了本资源有侵权行为怎么办?
    及时联系客服QQ:2580505920,核实予以删除。

    参与讨论

    • 211会员总数(位)
    • 3737资源总数(个)
    • 0本周发布(个)
    • 0 今日发布(个)
    • 869稳定运行(天)

    欢迎加入「极客文库」,成为原创作者从这里开始!

    立即加入 了解更多
    成为赞助用户享有更多特权立即升级