一个不安分的JDBC驱动

news/2024/5/18 23:27:17 标签: jdbc
连接, 连接, 总是连接!

  生活中肯定有比数据库连接更有趣的事情。

  1

  数据库连接

  又到了数据库连接的时间!

  那些码农把数据库参数送过来, Oracle , Db2, Sybase, SQL Server这些JDBC Driver 懒洋洋起来去干活赚钱。

  小东也是其中之一, 每天的工作就是连接Mysql 数据库, 发出SQL查询, 获取结果集。

  工作稳定, 收入不菲, 只是日复一日,年复一年, 枯燥的工作实在是太令人厌烦了。

  有时候小东会和其他JDBC Driver 聊天, 谈起有些不着调的码农, 创建一个Connection, 发出一个查询, 处理完ResultSet后 , 立刻就把Connection给关掉了。

  “他们简直不知道我们建立一个数据连接有多么辛苦, 先通过Socket 建立TCP连接, 然后还要有应用层的握手协议, 唉, 不知道要干多少脏活累活, 这帮码农用完就关, 真是浪费啊。 ”

  “还有更可气的, 有些家伙使用了PreparedStatement , 我通知数据库做了预编译, 制定了查询计划, 为此我还花费了不菲的小费。 但是只执行了一次查询, Connection就关掉了, PreparedStatement 也就不可用了, 现在数据库都懒的给我做预编译了 !”

  “你们这都是好的, 有些极品根本就不关闭Connection, 最后让这个Connection 进入了不可控状态。 ”

  “我们啊, 都把宝贵的生命都献给了数据库连接事业...... ”

  抱怨归抱怨, 大部分人都安于现状,逆来顺受了。

  2

  向Tomcat取经

  但是不安分的小东决心改变, 他四处拜访取经, 但是一无所获。

  这一天在Tomcat村遇到了Tomcat 村长, 看到了村长处理Http请求的方式, 突然间看到了曙光。

  村长说: 我们本来是一个线程处理一个Http请求 , 一个Http请求来到我们这里以后, 我并不会新建一个线程来处理, 而是从一个小黑屋叫来一个线程直接干活, 干完活以后再回到小黑屋待着。

  小东问: 小黑屋是什么?

  (码农翻身注: 参见文章《我是一个线程》)

  村长说: “学名是线程池, 为了充分利用资源, 我在启动时就建立了一批线程, 放到线程池里, 需要线程时直接去取就可以了。 ”

  “那要是线程池里的线程都被派出去了怎么办 ? ”

  "要么新创建线程, 要么新来的Http请求就要等待了。 实际上,线程也不是无限可用的资源, 也得复用。"

  小东心想, 我们JDBC也可以这么搞啊, 把数据库连接给缓存下来, 随用随取, 一来正好可以控制码农们无限制的连接数据库; 二来可以减少数据库连接时间; 第三还可以复用Connection上的PreparedStatement, 不用老是找数据库预编译了。

  3

  数据库连接池

  建立数据库连接池不是那么一帆风顺的, 小东的第一次尝试是创建了一个ConnectionPool这个接口:

  

  里边有两个重要的方法, getConnection(), 用于从池中取出一个让用户使用;

  releaseConnection() 把数据库连接放回池中去。

  小东想, 只要我写一个ConnectionPool的实现类, 里边可以维护一个管理数据库连接的数据结构就行了, 码农们用起来也很方便, 他们肯定会喜欢的。

  可是事与愿违, 几乎没有人用这样的接口。

  小东经过多方打探才明白, 码农们要么是用DriverManager来获得Connection, 要么是使用DataSource来得到Connection;关闭的时候,只需要调用Connection.close() 就可以了。

  这样的代码已经有很多了, 而小东的新方案相当于新的接口, 需要改代码才能用, 话说回来, 谁愿意没事改代码玩? 尤其是正在运行的系统。

  再做一次改进吧, 小东 去找Java 这个设计高手求教。

  Java 说:“虽然ConnectionPool概念不错, 但是具体的实现最好隐藏起来, 对码农来说,还是通过DataSource 来获取Connection, 至于这个Connection 是新建的还是从连接池中来的, 码农不应该关心, 所以应该加一个代理Proxy,把物理的Connection给封装起来, 然后把这个Proxy返回给码农。”

  “那这个Proxy是不是得和您定义的接口Connection 接口保持一致? 要不然码农还得面对新东西。”

  “是的, 这个Proxy 应该也实现JDBC的Connection 接口, 像这样: ”

  

  (点击看大图)

  小东说: ”奥, 我明白了, 当码农从DataSource中获得Connection的时候, 我返回的其实是一个ConnectionProxy , 其中封装了一个从ConnectionPool来的Connection , 然后把各种调用转发到这个实际的physicalConn的方法去, 关键点在close, 并不会真的关闭Connection, 而是放回到ConnectionPool “

  “哈哈, 看来你已经get了, 这就是面向接口编程的好处啊, 你给码农返回了一个ConnectionProxy, 但是码农们一无所知, 仍然以为是在使用Connection , 这是一次成功的‘欺骗’啊”

  “但是你定义的Connection 接口中的方法实在是太多了, 足足有50多个, 我这个Proxy类实际上只关注那么几个, 例如close方法, 其他的都是转发而已,这么多无聊的转发代码是在是太烦人了”

  Java说: “还有一种办法,可以用动态代理啊”

  小东问:“什么是动态代理?”

  "刚才我们提供的那个Proxy可以称为静态代理, 我的动态代理不用你写一个类去实现Connection, 完全可以在运行期来做, 还是先来看代码吧"

  

  (点击看大图)

  “代码有点难懂, 你看,这里没有声明一个实际的类来实现Connection 接口 , 而是用动态代理在运行时创建了一个类Proxy.newProxyInstance(....) , 重点部分就是InvocationHandler, 在他的invoke方法中, 我们判断下被调用的方法是不是close, 如果是, 就把Connection 放回连接池, 如果不是,就调用实际Connection的方法。” Java 解释了一通。

  小东惊叹到:“代码虽然难懂, 但是精简了好多,我对Java 反射不太熟, 回头我再仔细研究下。”

  (码农翻身注: 不熟悉Java动态代理的也可以研究下, 这是一项重要的技术)

  经过一番折腾, 数据库连接池终于隐藏起来了, 码农们可以使用原有的方式去获取Connection, 只是不知道背后其实发生了巨变。

  当然也不可能让码农完全意识不到连接池, 毕竟他们还得去设置一些参数, 小东定义了一些:

  

  数据库连接池获得了巨大的成功, 几乎成了每一个Java Web项目的标配, 不一样的JDBC驱动小东也获得了极高的荣誉, 后面等着他的还会有哪些挑战呢?

  (完)


http://www.niftyadmin.cn/n/813240.html

相关文章

Mybatis 二(1)之高级映射(延迟加载)

1. 一对一查询(resultType、resultMap实现): resultType: 需要自定义一个 POJO类的扩展类,保证SQL查询列与POJO中属性对应; resultMap: 配置 .xml映射文件,一对一映射使用 associat…

解决ubuntu(16.04版本)和windows电脑之间无法复制粘贴问题

1.执行下列命令: sudo apt-get autoremove open-vm-toolssudo apt-get install open-vm-tools-desktop安装过程中,yes或者y一路通过。 2.然后重启ubuntu就可以了。 — 亲测有效,屡试不爽

Django-- (五) Django接口开发

1、前后端分离 1.1 传统的开发模式 传统的开发模式流程:根据url访问视图函数,在视图函数中进行逻辑判断、调用数据库、渲染HTML,最后再向浏览器返回HTML页面。之前功能的开发使用的就是传统开发模式。但有时候我们需要将这些内容在移动端&a…

MyBatis 二(2)之缓存

缓存: 缓存是将用户经常需要查询的数据放入到内存(缓存)中,用户不需要经常访问磁盘,提高查询效率,解决并发性能问题;MyBatis 持久层缓存 MyBatis 提供一级缓存和二级缓存: ① MyBat…

JDBC后传

抱怨 JDBC出现以后, 以其对数据库访问出色的抽象, 良好的封装, 特别是把接口和具体分开的做法, 赢得了一片称赞。 (参见文章《JDBC的诞生》) 乘着Java和互联网的东风, JDBC在风口飞了起来&#…

django项目开发过程中基本的Python脚本编写

1.示例及讲解: #!/usr/bin/env python # 指定脚本运行环境 # #!codingutf-8 #输入这一条就可以在Python脚本里面使用汉语注释!此脚本可以直接复制使用; import timeimport sys sys.path.insert(0, ../) # 注意脚本执行过程中可能涉及到…

Django-- (六) Django高级开发

1、自定义过滤器 Django提供了很多过滤器,但有时并不能满足我们的需求,这时候就需要自定义过滤器来实现某种功能。 1.1 步骤 1、创建templatetags包 名字是固定的,不能随意改动 2、 在templatetags包里创建文件,用来存放过滤器…

利用Flume拦截器(interceptors)实现Kafka Sink的自定义规则多分区写入

我们目前的业务场景如下:前端的5台日志收集服务器产生网站日志,使用Flume实时收集日志,并将日志发送至Kafka,然后Kafka中的日志一方面可以导入到HDFS,另一方面供实时计算模块使用。 前面的文章《Kafka分区机制介绍与示…