笔记-JDBC和commons-dbutils
1、前言
玩过Java web的人应该都接触过JDBC,正是有了它,Java程序才能轻松地访问数据库。JDBC很多人都会,但是为什么我还要写它呢?我曾经一度用烂了JDBC,一度认为JDBC不过如此,后来,我对面向对象的理解渐渐深入,慢慢地学会了如何抽象JDBC代码,再后来,我遇到了commons-dbutils这个轻量级工具包,发现这个工具包也是对JDBC代码的抽象,而且比我写的代码更加优化。在这个过程中,我体会到了抽象的魅力,我也希望通过这篇文章,把我的体会分享出来。
文章大致按一定的逻辑进行:JDBC如何使用-----这样使用有什么问题------如何改进-----分析commons-dbutils的原理
2、JDBC如何使用
这一小节通过一个例子来说明JDBC如何使用。
我们大致可以讲JDBC的整个操作流程分为4步:
1、获取数据库连接
2、创建statement
3、执行sql语句并处理返回结果
4、释放不需要的资源
下面是一个小例子(省略了try-catch代码):
String username="root";
String password="123";
String url="jdbc:mysql://localhost/test";
Connection con=null;
Statement st=null;
ResultSet rs=null; //1、获取连接
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager.getConnection(url,username,password); //2、创建statement
String sql="select * from test_user";
st=con.createStatement(); //3、执行sql语句并处理返回结果
rs=st.executeQuery(sql);
while(rs.next())
{
//对结果进行处理
} //4、释放资源
rs.close();
st.close();
con.close();
以上的例子是查询的一种用法,除了用Statement外,还可以用PreparedStatement,后者是前者的子类,在前者的基础上增加了预编译和防止sql注入的功能。另外,查询和增删改是不同的用法,查询会返回ResultSet而增删改不会。
3、这样写代码有什么问题
3.1、这样写代码会造成大量重复劳动,比如获取连接,如果每个执行sql的方法都要写一遍相同的代码,那么这样的重复代码将充斥整个DAO层。
3.2、这样的代码可读性比较差,几十行代码真正和业务相关的其实就几行
3.3、大量重复代码会造成一个问题,那就是可维护性变差,一旦某个常量改变了,那么就需要把每个方法都改一遍
3.4、数据库连接是重量级资源,每调用一次方法都去创建一个连接,性能会存在瓶颈
4、如何改进
针对前面的问题中的1、2、3,改进的方法就是抽象,把可重用的代码抽象出去,单独组成一个模块,模块与模块之间实现解耦。由于整个JDBC操作流程分为4步,因此可以从这4步中下手去抽象。
4.1、获取数据库连接
我当时的解决方案是一次初始化很多连接放入list,然后用的时候取,现在的通用方法就是连接池,比如DBCP、C3P0等等。有兴趣的人可以去看看它们的源代码,看看是如何实现的
4.2、创建statement
我当时使用PreparedStatement进行处理,因为PreparedStatement会缓存已经编译过的sql
4.3、执行sql语句并处理返回结果
这块可以使用反射,将得到的结果封装成Java bean对象
4.4、释放资源
使用动态代理,改变connection的close方法的行为,将connection放回连接池
5、commons-dbutils的原理
虽然我做出了改进,但距离真正的解耦还差得远,而commons-dbutils作为commons开源项目组中的一个成员,在这方面做得还算不错,通过阅读它的源代码,可以学习如何抽象和解耦JDBC的操作流程。
5.1、整体结构
先看一下它有哪些类:
一共有27个类,但真正常用的是三大组件十几个类:门面组件、结果处理组件和行处理组件,其中门面组件提供程序入口,并进行一些参数检验等,结果处理组件则是核心所在,因为返回的结果可以是map,可以是list可以是JavaBean,这一块的变化很大,所以抽象出一个组件出来应对这些变化,行处理组件是从结果处理组件中分离出来的,它是结果处理组件的基础,无论哪种处理器,最终都要与一行数据打交道,因此,单独抽象出这一组件。
类名 | 描述 |
门面组件 | |
QueryRunner | 执行增删改查的入口 |
结果处理组件 | |
ResultSetHandler | 用于处理ResultSet的接口 |
AbstractKeyedHandler | 将返回结果处理成键值对的抽象类 |
KeyedHandler |
处理数据库返回结果,封装成一个Map,数据库表的一个列名为key,通常可以用主键,数据库中的一行结果以Map的形式作为value |
BeanMapHandler | 处理数据库返回结果,封装成一个Map,和KeyedHandler的唯一的不同是,每一行结果以Javabean的形式作为value |
AbstractListHandler | 将返回结果处理成链表的抽象类 |
ArrayListHandler |
将返回结果处理成链表,这个链表的每个 元素都是一个Object数组,保存了数据库中对应的一行数据 |
ColumnListHandler |
如果要取单独一列数据,可以用这个handler,用户指定列名,它返回这个 列的一个list |
MapListHandler |
和ArrayListHandler不同的是,链表的每个元素是个Map,这个Map代表数据库里的一行数据 |
ArrayHandler |
将一行数据处理成object数组 |
BeanHandler |
将一行数据处理成一个Java bean |
BeanListHandler |
将所有数据处理成一个list,list的元素时Java bean |
MapHandler |
将一行结果处理成一个Map |
MapListHandler |
将所有结果处理成一个list,list的元素时Map |
ScalarHandler |
这个类常常用于取单个数据,比如某一数据集的总数等等 |
行处理组件 |
|
RowProcessor | 用于处理数据库中一行数据的接口 |
BasicRowProcessor | 基本的行处理器实现类 |
BeanProcessor | 通过反射将数据库数据转换成Javabean |
工具类 | |
DbUtils | 包含很多JDBC工具方法 |
5.2 执行流程
无论是增删改查,都需要调用QueryRunner的方法,因此QueryRunner就是执行的入口。它的每个方法,都需要用户提供connection、handler、sql以及sql的参数,而返回的则是用户想要的结果,这可能是一个List,一个Javabean或者仅仅是一个Integer。
1、以查询为例,QueryRunner内部的每一个查询方法都会调用私有方法,先去创建 PreparedStatement,然后执行sql得到ResultSet,然后用handler对结果进行处理,最后释放连接,代码如下:
1 private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params)
2 throws SQLException {
3 if (conn == null) {
4 throw new SQLException("Null connection");
5 }
6
7 if (sql == null) {
8 if (closeConn) {
9 close(conn);
10 }
11 throw new SQLException("Null SQL statement");
12 }
13
14 if (rsh == null) {
15 if (closeConn) {
16 close(conn);
17 }
18 throw new SQLException("Null ResultSetHandler");
19 }
20
21 PreparedStatement stmt = null;
22 ResultSet rs = null;
23 T result = null;
24
25 try {
26 stmt = this.prepareStatement(conn, sql); //创建statement
27 this.fillStatement(stmt, params); //填充参数
28 rs = this.wrap(stmt.executeQuery()); //对rs进行包装
29 result = rsh.handle(rs); //使用结果处理器进行处理
30
31 } catch (SQLException e) {
32 this.rethrow(e, sql, params);
33
34 } finally {
35 try {
36 close(rs);
37 } finally {
38 close(stmt);
39 if (closeConn) {
40 close(conn);
41 }
42 }
43 }
44
45 return result;
46 }
2、每个handler的实现类都是以抽象类为基础,看代码(以AbstractListHandler为例):
1 @Override
2 public List<T> handle(ResultSet rs) throws SQLException {
3 List<T> rows = new ArrayList<T>();
4 while (rs.next()) {
5 rows.add(this.handleRow(rs));
6 }
7 return rows;
8 }
9
10 /**
11 * Row handler. Method converts current row into some Java object.
12 *
13 * @param rs <code>ResultSet</code> to process.
14 * @return row processing result
15 * @throws SQLException error occurs
16 */
17 protected abstract T handleRow(ResultSet rs) throws SQLException;
handle方法都是一样的,这个方法也是QueryRunner内部执行的方法,而不一样的在handleRow这个方法的实现上。这里用到了模板方法的设计模式,
将不变的抽象到上层,易变的下方到下层。 3、每个handleRow的实现都不一样,但最终都会使用行处理器组件,行处理器是BasicRowProcessor,有toArray,toBean,toBeanList,toMap这些方法
toArray和toMap是通过数据库的元数据来实现的,而toBean和toBeanList则是通过反射实现,具体可以去看源代码实现,应该是比较好理解的。 5.3、和数据源的结合
从上面可以看出,dbutils抽象了2、3、4(JDBC 4步骤),而没有把连接的获取抽象,其实,连接的获取和维护本身就有其他组件提供,也就是datasource
数据源,dbutils只负责2、3、4,不该它管就不管,这样才能做到解耦。在构造QueryRunner的时候,可以选择传入一个数据源,这样,在调用方法的时候,
就不需要传入connection了。 5.4、总结
使用dbutils再加上DBCP数据源,可以极大的简化重复代码,提高代码可读性和可维护性,以下是使用dbutils的一个小例子:
1 /**
2 * 获取常用地址
3 * */
4 public List<CommonAddr> getCommAddrList(int memID) {
5 String sql = "SELECT `addrID`, `addr`, `phone`, `receiver`, `usedTime` "
6 + "FROM `usr_cm_address` WHERE `memID`=? order by usedTime desc";
7
8 try {
9 return runner.query(sql, new BeanListHandler<CommonAddr>(CommonAddr.class),memID);
10 } catch (SQLException e1) {
11 logger.error("getCommAddrList error,e={}",e1);
12 }
13 return null;
14 }
如果用最原始的JDBC来写,光把数据库结果转换成List估计都要十几行代码吧。
6、尾声
从JDBC到dbutils,实现的功能没有变,但是代码却简洁了,程序与程序之间的关系也更清晰了,这,也许就是面向对象的精髓吧~
文章出自:http://www.cnblogs.com/biakia/p/4300275.html
笔记-JDBC和commons-dbutils的更多相关文章
- 高性能jdbc封装工具 Apache Commons DbUtils 1.6(转载)
转载自原文地址:http://gao-xianglong.iteye.com/blog/2166444 前言 关于Apache的DbUtils中间件或许了解的人并不多,大部分开发人员在生成环境中更多的 ...
- JDBC 学习笔记(四)—— 自定义JDBC框架+Apache—DBUtils框架+事务管理+操作多表
本文目录: 1.自定义JDBC框架 ——数据库元数据:DataBaseMetaData 2.自定义JDBC框架 ——数据库元数据:DataBaseMetaData ...
- java JDBC (六) org.apache.commons.dbutils 增删改
dbutils是apache封装了JDBC的工具类,比mysql-connector更方便些 下载地址:http://commons.apache.org/proper/commons-dbutils ...
- java JDBC (七) org.apache.commons.dbutils 查询
package cn.sasa.demo1; import java.sql.Connection; import java.sql.SQLException; import java.util.Li ...
- Apache Commons DbUtils 快速上手
原文出处:http://lavasoft.blog.51cto.com/62575/222771 Hibernate太复杂,iBatis不好用,JDBC代码太垃圾,DBUtils在简单与优美之间取得了 ...
- 写一个ORM框架的第一步(Apache Commons DbUtils)
新一次的内部提升开始了,如果您想写一个框架从Apache Commons DbUtils开始学习是一种不错的选择,我们先学习应用这个小“框架”再把源代码理解,然后写一个属于自己的ORM框架不是梦. 一 ...
- Java -- JDBC 学习--使用 DBUtils
Apache—DBUtils简介 commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdb ...
- Java连接数据库 #04# Apache Commons DbUtils
索引 通过一个简单的调用看整体结构 Examples 修改JAVA连接数据库#03#中的代码 DbUtils并非是什么ORM框架,只是对原始的JDBC进行了一些封装,以便我们少写一些重复代码.就“用” ...
- 【JDBC&Dbutils】JDBC&JDBC连接池&DBUtils使用方法(重要)
-----------------------JDBC---------- 0. db.properties文件 driver=com.mysql.jdbc.Driver url=jdbc: ...
- 《笔者带你剖析Apache Commons DbUtils 1.6》(转)
前言 关于Apache的DbUtils中间件或许了解的人并不多,大部分开发人员在生成环境中更 多的是依靠Hibernate.Ibatis.Spring JDBC.JPA等大厂提供的持久层技术解决方案, ...
随机推荐
- 短视频图像处理 OpenGL ES 实践
2017年,短视频正以其丰富的内容表现力和时间碎片化的特点,快速崛起,而短视频最具可玩性之处就在支持人脸识别的动态贴图和各种不同效果的美颜.滤镜等.那短视频动态贴纸.滤镜.美颜等功能究竟是如何实现的呢 ...
- 谈一谈synchronized关键词
1.使用 java中的每一个对象都可以作为synchronized的锁进行代码同步,常见的形式 同步代码块锁是synchronized括号内的对象 普通成员方法上,锁是当前的对象,synchroniz ...
- 【轉】JS,Jquery获取各种屏幕的宽度和高度
Javascript: 网页可见区域宽: document.body.clientWidth网页可见区域高: document.body.clientHeight网页可见区域宽: document.b ...
- C语言编译过程及数据类型
写在前面 C语言可以称得上是高级语言中的低级语言,接下来一段时间,我会写一下文章关于c语言,把它的神秘面纱一 一揭开.下面主要是c语言的C语言编译过程及数据类型 源文件编译过程 为了使计算机能执行高级 ...
- C++运算符优先级 案例1
问: ... short nReaderCount=10 ++pLock->nReaderCount==? ...++和->同为1级优先级,我想很多也有很多新手弄 ...
- Windows 编程中恼人的各种字符以及字符指针类型
在Windows编程中,很容易见到这些数据类型:LPSTR,LPTSTR,LPCTSTR... 像很多童鞋一样,当初在学Windows编程的时候,对着些数据类型真的是丈二和尚,摸不着头脑,长时间不用就 ...
- Bmob云IM实现头像更换并存入Bmob云数据库中(1.拍照替换,2.相册选择)
看图效果如下: 1.个人资料界面 2.点击头像弹出对话框 3.点击拍照 4.切割图片,选择合适的部分 5.点击保存,头像替换完毕,下面看从相册中选择图片. 6.点击相册 7.任选一张图片 8.切割图片 ...
- P1050
问题 F: P1050 时间限制: 1 Sec 内存限制: 128 MB提交: 37 解决: 27[提交][状态][讨论版] 题目描述 一个字符串A的子串被定义成从A中顺次选出若干个字符构成的串. ...
- MySQL之增删改查
前言:以下是MySQL最基本的增删改查语句,很多IT工作者都必须要会的命令,也是IT行业面试最常考的知识点,由于是入门级基础命令,所有所有操作都建立在单表上,未涉及多表操作. 前提:在进行" ...
- 再起航,我的学习笔记之JavaScript设计模式30(简单模板模式)
简单模板模式 概念介绍 简单模板模式(Simple template): 通过格式化字符串拼凑出视图避免创建视图时大量节点操作,优化内存开销. 创建模板 在实际的业务中如果我们需要进行前后台交互,或多 ...