返回介绍

一个不安分的 JDBC 驱动

发布于 2025-01-22 00:38:52 字数 4035 浏览 0 评论 0 收藏 0

连接, 连接, 总是连接!

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

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 驱动小东也获得了极高的荣誉, 后面等着他的还会有哪些挑战呢?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文