2019独角兽企业重金招聘Python工程师标准>>>

本文就是对我问的问题:http://www.oschina.net/question/1029535_127629?p=1#AnchorAnswer597790 的一个解释。

dbutil包为我们提供了BasicRowProcessor类,用来将查询结果集进行封装。其中有一个toMap方法,该方法的作用就是将结果集转化成一个Map,结果集中的字段名对应map的key,字段值对应map中的value。该方法的独特之处在于:获取的map的key是大小写不敏感的。

先看一下toMap方法:

/*** Convert a <code>ResultSet</code> row into a <code>Map</code>.  This* implementation returns a <code>Map</code> with case insensitive column* names as keys.  Calls to <code>map.get("COL")</code> and* <code>map.get("col")</code> return the same value.* @see org.apache.commons.dbutils.RowProcessor#toMap(java.sql.ResultSet)* @param rs ResultSet that supplies the map data* @throws SQLException if a database access error occurs* @return the newly created Map*/@Overridepublic Map<String, Object> toMap(ResultSet rs) throws SQLException {Map<String, Object> result = new CaseInsensitiveHashMap();ResultSetMetaData rsmd = rs.getMetaData();int cols = rsmd.getColumnCount();for (int i = 1; i <= cols; i++) {result.put(rsmd.getColumnName(i), rs.getObject(i));}return result;}

该方法里用到了一个类CaseInsensitiveHashMap,在没看该方法的实现之前,我以为它大致应该是这个样子的:

//下面是按照我的思路实现的
public static class CaseInsensitiveHashMap extends HashMap<String, String> {private static final long serialVersionUID = -2127888215578997896L;@Overridepublic String get(Object key) {return super.get(key.toString().toLowerCase(Locale.ENGLISH));}@Overridepublic boolean containsKey(Object key) {return super.containsKey(key.toString().toLowerCase(Locale.ENGLISH));}@Overridepublic String put(String key, String value) {return super.put(key.toString().toLowerCase(Locale.ENGLISH), value);}// 其他省略。。}

思路很简单,就是在put/get/containsKey方法的时候,将传进来的key值统一转化为小写,这样不就能达到大小写不敏感了吗?没错,这样做是对的!而且apache官方的代码中,最开始也是这样做的!(后面我会给出官方最开始的代码)

但是后来我看了一下apache的代码,顿时让我惊呆了,好麻烦,他们并没有按照我的思路实现,他们的思路是:

首先在CaseInsensitiveHashMap中定义一个lowerCaseMap,它是一个普通的hashMap:

/*** The internal mapping from lowercase keys to the real keys.** <p>* Any query operation using the key* ({@link #get(Object)}, {@link #containsKey(Object)})* is done in three steps:* <ul>* <li>convert the parameter key to lower case</li>* <li>get the actual key that corresponds to the lower case key</li>* <li>query the map with the actual key</li>* </ul>* </p>*/private final Map<String, String> lowerCaseMap = new HashMap<String, String>();

它的作用是存放真实的字段名与其转化为小写后的字段名的对应关系,举个例子来讲就是:如果字段名为tbl_myName,那么lowerCaseMap中就会保存一个key为tbl_myname(小写),value为tbl_myName的键值对。

然后看看他们的其他方法实现:

(1)put方法:

/** {@inheritDoc} */@Overridepublic Object put(String key, Object value) {/** In order to keep the map and lowerCaseMap synchronized,* we have to remove the old mapping before putting the* new one. Indeed, oldKey and key are not necessaliry equals.* (That's why we call super.remove(oldKey) and not just* super.put(key, value))*/Object oldKey = lowerCaseMap.put(key.toLowerCase(Locale.ENGLISH), key);Object oldValue = super.remove(oldKey);super.put(key, value);return oldValue;}

(2)get方法:

/** {@inheritDoc} */@Overridepublic Object get(Object key) {Object realKey = lowerCaseMap.get(key.toString().toLowerCase(Locale.ENGLISH));return super.get(realKey);}

其他方法与上面两个方法类似,不做介绍。

put方法很关键:它要负责维护两个Map,一个就是存放小写的字段名与字段值的map,通过这个super.put(key,value)实现,另一个就是要维护lowerCaseMap。

看到这里我就觉得很奇怪了,为什么要这么麻烦呢?用我的方式不也能达到同样的目的吗?干嘛还要在维护一个额外的map?.....

莫非人家的设计有问题?肯定不是,一定有原因,于是我就开始了探索。。。找啊找啊找。。。

找了半天,终于找到答案了:仔细看一下他们CaseInsensitiveHashMap的注释:

/*** A Map that converts all keys to lowercase Strings for case insensitive* lookups.  This is needed for the toMap() implementation because* databases don't consistently handle the casing of column names.** <p>The keys are stored as they are given [BUG #DBUTILS-34], so we maintain* an internal mapping from lowercase keys to the real keys in order to* achieve the case insensitive lookup.** <p>Note: This implementation does not allow <tt>null</tt>* for key, whereas {@link HashMap} does, because of the code:* <pre>* key.toString().toLowerCase()* </pre>*/private static class CaseInsensitiveHashMap extends HashMap<String, Object> {

哦,原来是为了解决这个bug [BUG #DBUTILS-34],链接:https://issues.apache.org/jira/browse/DBUTILS-34

原来,如果采用我的那种实现思路,map里存放的key将全部是小写的字段名,这样的话,如果用户调用toMap方法获得了map,然后想获取数据库中真实字段名,这个时候问题就来了:我们把数据库中的真是字段名都转为了小写,那么用户怎么知道真实字段名是什么呢?肯定没办法知道啊。所以为了提供给用户更详细的信息,他们将代码改成了现在的模样:

添加一个lowerCaseMap来保存原始字段名和转换成小写后的字段名的对应关系,然后将真实的字段名和字段值作为键值对存放起来。每次get(key)的时候,先从lowerCaseMap获取到原始字段名,然后在调用super.get(原始字段名)获取值。

这样一来,用户获得Map后,如果使用Map.Entry<K,V>的Iterator,这时候,他们获得的Key值就是数据库中真实的字段名了~~~所以,既达到了忽略大小写的目的,又能提供给用户足够的信息,多么精彩的设计啊!

PS:我的解释可能不是特别清楚,但那个bug里已经讲得很明白了,大家可以自己看一下。

前面讲到过了,最初的时候apache也是按照我的思路实现的,可以看一下他们对CaseInsensitiveHashMap的patch记录:

说明:每行前面的减号表示为了改当前bug而去掉的代码(也就是最初的实现代码哦),加号表示新增代码!

Index: BasicRowProcessor.java
===================================================================
--- BasicRowProcessor.java  (revision 536476)
+++ BasicRowProcessor.java   (working copy)
@@ -144,10 +144,36 @@* A Map that converts all keys to lowercase Strings for case insensitive* lookups.  This is needed for the toMap() implementation because * databases don't consistenly handle the casing of column names.
+     *
+     * <p>The keys are stored as they are given [BUG #DBUTILS-34], so we maintain
+     * an internal mapping from lowercase keys to the real keys in order to
+     * achieve the case insensitive lookup.
+     *
+     * <p>Note: This implementation does not allow <tt>null</tt>
+     * for key, whereas {@link HashMap} does, because of the code:
+     * <pre>
+     * key.toString().toLowerCase()
+     * </pre>*/private static class CaseInsensitiveHashMap extends HashMap {/**
+         * The internal mapping from lowercase keys to the real keys.
+         *
+         * <p>
+         * Any query operation using the key
+         * ({@link #get(Object)}, {@link #containsKey(Object)})
+         * is done in three steps:
+         * <ul>
+         * <li>convert the parameter key to lower case</li>
+         * <li>get the actual key that corresponds to the lower case key</li>
+         * <li>query the map with the actual key</li>
+         * </ul>
+         * </p>
+         */
+        private Map lowerCaseMap = new HashMap();
+
+        /*** Required for serialization support.* * @see java.io.Serializable
@@ -158,21 +184,37 @@* @see java.util.Map#containsKey(java.lang.Object)*/public boolean containsKey(Object key) {
-            return super.containsKey(key.toString().toLowerCase());
+            Object realKey = lowerCaseMap.get(key.toString().toLowerCase());
+            return super.containsKey(realKey);
+            // Possible optimisation here:
+            // Since the lowerCaseMap contains a mapping for all the keys,
+            // we could just do this:
+            // return lowerCaseMap.containsKey(key.toString().toLowerCase());}/*** @see java.util.Map#get(java.lang.Object)*/public Object get(Object key) {
-            return super.get(key.toString().toLowerCase());
+            Object realKey = lowerCaseMap.get(key.toString().toLowerCase());
+            return super.get(realKey);}/*** @see java.util.Map#put(java.lang.Object, java.lang.Object)*/public Object put(Object key, Object value) {
-            return super.put(key.toString().toLowerCase(), value);
+            /*
+             * In order to keep the map and lowerCaseMap synchronized,
+             * we have to remove the old mapping before putting the
+             * new one. Indeed, oldKey and key are not necessaliry equals.
+             * (That's why we call super.remove(oldKey) and not just
+             * super.put(key, value))
+             */
+            Object oldKey = lowerCaseMap.put(key.toString().toLowerCase(), key);
+            Object oldValue = super.remove(oldKey);
+            super.put(key, value);
+            return oldValue;}/**
@@ -191,7 +233,8 @@* @see java.util.Map#remove(java.lang.Object)*/public Object remove(Object key) {
-            return super.remove(key.toString().toLowerCase());
+            Object realKey = lowerCaseMap.remove(key.toString().toLowerCase());
+            return super.remove(realKey);}}

怎么样,如果把前面有加号的行去掉,代码就跟我的实现是一样的啦~~~

下面来一点方法学,来记录一些新的学习方法:

1、事出无常必有妖:每一个不同寻常的地方一定是有原因的!我们要抓住这些不同寻常的细节,追其原因,这样才能找出真相!(如果我真的认为他们的实现是多此一举,而不继续追究,那么也就不能明白他们这样设计的真正原因了)

2、多看源码不仅能让你更透彻的理解一个类库的实现原理,而且在你使用的时候也能够更好的取舍!

3、没事去这些开源类库的jira看看,你会得到更多的灵感!

5、以后多写博客,因为很多时候自己心里明白,但是转化成语言却有一定困难,要多锻炼!!

转载于:https://my.oschina.net/jasonultimate/blog/165640

【源码阅读】dbutil包中BasicRowProcessor内部类CaseInsensiti...相关推荐

  1. java中arraycopy的用法_[jdk源码阅读系列]Java中System.arraycopy()的用法

    本文转载,原文链接: 3分钟了解Java中System.arraycopy的用法 - 伊万夫斯基 - 博客园  https://www.cnblogs.com/benjieqiang/p/114288 ...

  2. sqlmap源码阅读系列init中的_cleanupOptions

    有很多人说sqlmap的源码很难,也有人说sqlmap的源码非常值得一读.我觉得这就像小马过河一样,你不读你就没有发言权.对我而言,截至目前,sqlmap的源码还在可以理解的范围内,至少要比unitt ...

  3. [PaddleSeg源码阅读] PaddleSeg Validation 中添加 Boundary IoU的计算(3)——添加Boundary IoU

    经过前面: PaddleSeg Validation 中添加 Boundary IoU的计算(1)--val.py文件细节提示 PaddleSeg Validation 中添加 Boundary Io ...

  4. MyBatis 源码阅读 -- 核心操作篇

    核心操作包是 MyBatis 进行数据库查询和对象关系映射等工作的包.该包中的类能完成参数解析.数据库查询.结果映射等主要功能.在主要功能的执行过程中还会涉及缓存.懒加载.鉴别器处理.主键自增.插件支 ...

  5. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  6. spark.mllib源码阅读:GradientBoostedTrees

    Gradient-Boosted Trees(GBT或者GBDT) 和 RandomForests 都属于集成学习的范畴,相比于单个模型有限的表达能力,组合多个base model后表达能力更加丰富. ...

  7. 文本匹配开山之作-DSSM论文笔记及源码阅读(类似于sampled softmax训练方式思考)

    文章目录 前言 DSSM框架简要介绍 模型结构 输入 Encoder层 相似度Score计算 训练方式解读 训练数据 训练目标 训练方式总结 DSSM源码阅读 训练数据中输入有负样本的情况 输入数据 ...

  8. Spring源码阅读之bean对象的创建过程

    Spring源码阅读之bean对象的创建过程 ​ Spring是通过IOC容器来管理对象的,该容器不仅仅只是帮我们创建了对象那么简单,它负责了对象的整个生命周期-创建.装配.销毁.这种方式成为控制反转 ...

  9. 封装成jar包_通用源码阅读指导mybatis源码详解:io包

    io包 io包即输入/输出包,负责完成 MyBatis中与输入/输出相关的操作. 说到输入/输出,首先想到的就是对磁盘文件的读写.在 MyBatis的工作中,与磁盘文件的交互主要是对 xml配置文件的 ...

最新文章

  1. python基础:python扩展包的安装方式
  2. osg中实现HUD(OSG初级篇1)
  3. [No000018]都在背单词,为啥学霸那么厉害-如何在一天内记200个单词?
  4. linux 如何对文件解压或打包压缩
  5. ORACLE HANDBOOK系列之九:时间与时区(Time and Time Zone)
  6. 统计次数c语言,C 语言统计关键字出现次数
  7. C++学习笔记:(十)异常
  8. sql not exists用法_SQL Server 2012 高级用法(一)
  9. 域名自动跳转不搭建服务器,宝塔搭建的服务器WEB系统环境如果做域名301跳转
  10. 利用优先级队列实现堆栈
  11. C++多线程强制终止
  12. 彻底搞懂阻塞、非阻塞、同步、异步
  13. oracle中decode函数,行转列
  14. LINUX下载编译pcre
  15. 南卡和声阔蓝牙耳机哪个比较好用?降噪效果好的蓝牙耳机推荐
  16. 设置合适的密码策略chage命令
  17. 安装磁盘阵列卡的方法
  18. 【OpenGL】笔记二十一、Alpha测试、混合测试
  19. html表单界面设计,ui界面表单设计的三个方面
  20. Python高级编程——13.垃圾回收机制

热门文章

  1. c语言能让制表符空9个字符码,C语言入门9-1-分类函数
  2. 哈夫曼算法(最优二叉树)
  3. ESP8266 建立TCPserver 进行数据互传
  4. opencv介绍+python调取图片
  5. eclipse设置jsp文件编码默认为UTF-8
  6. C语言-数据结构-可变长顺序表的删除操作
  7. 解决切换场景时NGUI图集资源未释放的问题
  8. 软件构架实践_阅读笔记01(1-3)
  9. 【转载】socket select模型
  10. 小熊的人生回忆(六)