微信号:coderising

介绍:工作15年的IBM软件架构师分享编程和职场的经验和教训, 不容错过.

JDBC后传

2016-12-12 20:18 刘欣

抱怨

JDBC出现以后, 以其对数据库访问出色的抽象, 良好的封装, 特别是把接口和具体分开的做法, 赢得了一片称赞。

(码农翻身注: 参见文章《JDBC的诞生》)


乘着Java和互联网的东风, JDBC在风口飞了起来, 无数的程序开始使用JDBC进行编程, 访问数据库村的数据库, 在数据库村,无论是大佬Oracle, 还是小弟Mysql都赚的盆满钵满。 


所谓物极必反, JDBC的代码写得多了, 它的弱点就暴露出来了, 很多码农抱怨道:“JDBC是在是太Low了”。


消息传到Tomcat村, Java 简直不相信自己的耳朵: “什么?  JDBC还很low ? 看来那帮码农没有用socket访问过数据库吧?!  那才叫low . ”


Tomcat 说 : “你是个标准的制定者, 写代码太少了,太不接地气了,  你看看这样的代码: ”


Java 把代码拿过来一看, 不由的倒吸了一口凉气: “ 代码怎么这么长啊, 似乎是有那么一点问题,  ‘噪声’似乎太多了, 把业务代码全给淹没了”


Tomcat说:”看来你也是个明白人啊, 为了正确的打开和关闭你定义的Connection , Statement, ResultSet 需要花很多功夫,  再加上那些异常处理, 一个50多行的程序, 真正做事的也就那么10几行而已, 这些琐碎代码太烦人了, 所以大家抱怨很low啊。 ”


Java表示同意: “不错, 可以想象, 如果代码中有大量这样的代码, 码农会抓狂的, 不过,” Java突然想到了些什么 , “其实这不是我的问题, 码农们抱怨错人了,   我作为一门语言, 所能提供的就是贴近底层(socket)的抽象, 这样通用性最强。  至于码农想消除这些重复代码, 完全可以再封装, 再抽象, 再分层啊


Tomcat想想也是, 在计算机世界里, 每个人都有分工, 不能强求别人做不喜欢也不擅长的事情,  看来这件事错怪Java了。 


JDBCTemplate


Java 预料的不错,  稍微有点追求, 不愿意写重复代码的码农都对JDBC做了封装, 例如写个DBUtil的工具类把打开数据库连接, 发出查询语句都封装了起来。 


码农的抱怨也渐渐平息了。 


有一天, 有个叫JDBCTemplate的人来到了Tomcat村找到了Java , 他自称是Rod Johnson派来专门用于解决JDBC问题的, 他提供了一个优雅而简洁的解决方案。 


(码农翻身注: Rod Johnson就是Spring 最初的作者)


JDBCTemplate说:  “尊敬的Java先生, 感谢您发明了JDBC, 让我们可以远程访问数据库村, 您也听说了不少对JDBC的抱怨吧, 我的主人Rod Johnson 也抱怨过, 不过他在大量的编程实践中总结了很多经验, 他认为数据库访问无外乎这几件事情:


指定数据库连接参数

打开数据库连接

声明SQL语句

预编译并执行SQL语句

遍历查询结果

处理每一次遍历操作

处理抛出的任何异常

处理事务

关闭数据库连接”


“我的主人认为” JDBCTemplate说, “开发人员只需要完成黑体字工作就可以了,剩下的事情由我来办“


“你们主人的总结能力很强, 把一个框架应该做的事情和用户应该做的事情区分开了  ” Java说


JDBCTemplate 看到Java 态度不错, 赶紧趁热打铁:  “ 我给你看个例子:”


Java 和之前那个传统的JDBC比较了一下, JDBCTemplate的方式的确是把注意力放到了业务层面: 只关注SQL查询, 以及把ResultSet和 User业务类进行映射, 至于如何打开/关闭Connection, 如何发出查询,JDBCTemplate在背后都给你悄悄的完成了,  完全不用码农去操心。 


“你在JDBC上做了不错的抽象和封装”   Java 说, “但是我还不明白JDBCTemplate 怎么创建出来的”


“这很简单, 你可以直接把它new 出来, 当然new 出来的时候需要一个参数, 就是javax.sql. DataSource, 这也是你定义的一个标准接口啊”

"当然, 我主人Rod Johnson推荐结合Spring 来使用,  可以轻松的把我‘注入’到各个你需要的地方去"。 


“明白了, 你们主人这是要推销Spring 啊, 那是什么东西?   ”


“简单的说,就是一个依赖注入和AOP框架, 功能强大又灵活。 具体的细节还得让我主人亲自来给您介绍了 ”


(码农翻身注: 参见《Spring 的本质系列(1) -- 依赖注入》和《Spring 的本质系列(1) -- AOP》)


“好吧, 不管如何, 我看你用起来还不错, 可以向大家推荐一下。”


O/R Mapping


JDBCTemplate这样对JDBC的封装 , 把数据库的访问向前推进了一大步, 但是Tomcat村和数据库村的很多有识之士都意识到: 本质的问题仍然没有解决!


这个问题就是面向对象世界和关系数据世界之间存在的巨大鸿沟。 

  

Tomcat村的Java 程序都是面向对象的: 封装、继承、多态,  对象被创建起来以后, 互相关联, 在内存中形成了一张图。   


数据库村的关系数据库则是表格:  主键,外键, 关系运算、范式、事务。  数据被持久化在硬盘上。 


ResultSet依然是对一个表的数据的抽象和模拟: rs.next() 获取下一行,  rs.getXXX("XX") 访问该行某一列。


把关系数据转化成Java对象的过程, 仍然需要码农们写大量代码来完成。  


现在码农的呼声越来越高, 要把这个过程给自动化了。 他们的要求很清晰: 我们只想用面向对象的方式来编程,  我们告诉你Java 类、属性 和数据库表、字段之间的关系,  你能不能自动的把对数据库的增删改查给实现了 ?    


他们还把这个诉求起了一个很洋气的名称:  O/R Mapping  (Object Relational Mapping)


Java 自然不敢怠慢,  赶紧召集Tomcat村和数据库村开了一次会议, 确定了这么几个原则:


1.  数据库的表映射为Java 的类(class)

2.  表中的行记录映射为一个个Java 对象

3.  表中的列映射为Java 对象的属性。 

但是光有这几个原则是远远不够的, 一旦涉及到实际编程, 细节会扑面而来:


1.  Java类的粒度要精细的多, 有时候多个类合在一起才能映射到一张表

 

例如下面的例子, User类 的name属性其实是也是一个类, 但在数据库User 表中,  firstName, middleName, lastName却是在同一张表中的。 

2. Java 的面向对象有继承, 而数据库都是关系数据, 根本没有继承这回事!


这时候可选的策略就很多了, 比如 

(1) 把父类和子类分别映射到各自的Table 中,  数据会出现冗余

(2) 把父类的公共属性放到一个Table中, 每个子类都映射到各自的Table中, 但是只存放子类自己的属性。子类和父类的表之间需要关联。

(3) 干脆把父类和子类都映射到同一张Table中, 用一个字段(例如Type)来表明这一行记录是什么类型。


3.  对象的标识问题

Java中使用a==b 或者 a.equals(b) 来进行对象是否相等的判断,  而数据库则是另外一套:使用主键。


4. 对象的关联问题

在Java 中, 一个对象关联到另外一个或者一组对象是在是太常见了, 双向的关联(也就是A 引用B , B反过来也引用了A )也时常出现,   而在数据库中定义关联能用的手段只剩下外键和关联表了。 


5. 数据导航

在OOP中, 多个对象组成了一张网, 顺着网络上的路径, 可以轻松的从一个对象到达另外一个对象。 例如: City c = user.getAddress().getCity(); 


在关系数据库中非得通过SQL查询, 表的连接等方式来实现不可。 


6. 对象的状态

在OOP中, 对象无非就是创建出来使用, 如果不用了,就需要回收掉, 但是一旦扯上数据库, 势必要在编程中引入新的状态,例如“已经持久化”

......


(码农翻身注: 本来想讲Hibernate, 但是限于篇幅, 实在是无法展开讲细节, 这几个问题是Hibernate 官网上提到的, 是O/R Maping 的本质问题)


这些细节问题让Java 头大,  他暗自思忖: " 还是别管那些码农的抱怨, 我还是守住JDBC这一亩三分地吧,  这些烦人的O/R Mapping 问题还是让别人去处理好了。  "


O/R Mapping 的具体实现就这么被Java 搁置下了。 


Hibernate 和 JPA


随着时间的推移,各大厂商都想利用Java 赚钱, 联合起来搞了一个叫J2EE的规范, 然后疯狂的推行自己的应用服务器(例如Weblogic, Websphere等等),还搭配着硬件销售,  在互联网泡沫时代赚了不少钱。 


J2EE中也有一个叫Entity Bean 的东西, 试图去搞定O/R Mapping ,  但其蹩脚的实现方式被码农们骂了个狗血喷头。 


转眼间, 时间到了2001年, Tomcat告诉Java 说: “听说了吗?  现在很多码农都被一个叫Hibernate的东西给迷住了”


"冬眠(Hibernate) ? 让内存中的数据在数据库里冬眠,  这个名字起的很有意境啊 , 我猜是一个O/R Mapping 工具吧" 


"没错, 是由一个叫Gavin King的小帅哥开发的,  这个框架很强悍, 它实现了我们之前讨论的各种烦人细节,  大家都趋之若鹜, 已经成为O/R Mapping 事实上的标准,  Entity Bean 已经快被大家抛弃了。 "


“没关系, Entity Bean 从1.0 开始就是一个扶不起的阿斗,  我已经想通了, 我这里只是指定标准, 具体的实现让别人去做。 既然Hibernate 这么火爆,  我们就把Gavin King 招安了吧 ”


“怎么招安? ”


“让小帅哥过来领导着大家搞一个规范吧, 参考一下Hibernate的成功经验 , 他应该会很乐意的。  ”


不久以后, 一个新的Java规范诞生了, 专门用于处理Java 对象的持久化问题, 这个新的规范就是JPA(Java Persistence API),  Hibernate 自然也实现了这个规范, 几乎就是JPA的首选了。  


码农翻身相关历史文章推荐:



计算机网络

我是一个路由器

我是一个网卡

TCP/IP之大明邮差

TCP/IP之大明内阁

TCP/IP之蓟辽督师

张大胖的socket

IE为什么把Chrome和火狐打伤了?

对浏览器村的第二次采访

节约标兵IE的自述

EMail诞生记

EMail诞生记(下)

数据库

小李的数据库之旅(上)

小李的数据库之旅(下)

张大胖学数据库

数据库村的旺财和小王

Java EE

Java:一个帝国的诞生

JDBC诞生记

JSP:一个装配工的没落

Javascript: 一个屌丝的逆袭

Spring本质系列(1) -- 依赖注入

Spring本质系列(2) -- AOP

Http 历险记(上)

Http 历险记(下)—Struts的秘密

三层架构和MVC那点事儿

Java帝国之 Java Bean(上)

Java帝国之 Java Bean(下)


你看到的只是冰山一角, 更多精彩文章,尽在“码农翻身” 微信公众号, 回复消息"m"或"目录" 查看更多文章


有心得想和大家分享? 欢迎投稿 ! 我的联系方式:微信:liuxinlehan  QQ: 3340792577


公众号:码农翻身

“码农翻身”公众号由工作15年的前IBM架构师创建,分享编程和职场的经验教训。


 
码农翻身 更多文章 JDBC的诞生 数据库村的旺财和小强 张大胖学数据库 从厨师到码农 JSP: 一个装配工的没落
猜您喜欢 国庆佳节,积分奖励拿不停!还有QQ公仔等着您哦~ 一名全栈工程师Node.js之路 [活动预告]10月20日,郑州 —— 不见不散 让你的设计拥有持久的生命力的7个技巧 创建一个兼容微信小程序的Web框架