简化JDBC的数据库访问

来源:互联网 发布:南师大软件中心 编辑:程序博客网 时间:2024/05/21 17:08

简化JDBC的数据库访问

问题的产生:

    在一些小型应用中,JDBC扮演着一个让人进退两难的角色。
    最近我遇到了这样一个问题:项目不大,犯不着使用Hibernate之类的持久化技术,但JDBC带来的大量重复代码着实让维护工作举步维艰。

    传统的数据库Query方法是这样的:

构造SQL语句 -> 从连接池中获取Connection -> 创建Statement -> 执行SQL查询请求 -> 处理ResultSet -> 关闭ResultSet以及Statement -> 释放Connection

    大致的代码如下:

  String SQL = "SELECT name, pass FROM table WHERE id = '"+ id +"'";

  Connection conn = ConnectionPool.getConnection();
  Statement stmt = conn.createStatement();
  ResultSet rs = stmt.exeQuery(SQL);

  while(rs.next()){
    //.......
  }

  rs.close();
  stmt.close();
  ConnectionPool.release(conn);

    这样带来一个问题:由于每次查询都要有这么一个套路,那么一旦改变了获取和释放Connection的策略,维护的工作量是巨大的。
    如果你也像我一样想减少Ctrl-c和Ctrl-v的次数,那么请继续看下去吧。

分析

    事实上我们所关注的只有两个地方
        1. 构造SQL语句
        2. 处理ResultSet过程
    其余部分都是简单的重复,按照重构的原则,因该尽量减少重复的代码,而把更多的注意力集中在对ResultSet的处理上。

    假设我们有一个叫做doQuery的函数,用来统一处理 Query过程。
    也许你会想到把构造好的SQL传递给doQuery,然后返回一个ResultSet,再另行处理ResultSet,这样的doQuery看起来会是这样:
public ResultSet doQuery (String SQL){
    Connection conn = ConnectionPool.getConnection();
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.exeQuery(SQL);
       
    stmt.close();        //ResultSet也同时被关闭了
    ConnectionPool.release(conn);
    return rs;
}
    看起来不错,但是这样做你是无法处理ResultSet的,因为在Statement关闭的同时ResultSet也被关闭了。所以必须在同一个函数里创建、处理并关闭ResultSet。

解决方案:

    解决方案是把构造好的SQL和处理ResultSet的过程同时传递给doQuery。可是,怎么实现呢?别忘了Java可是OO的。OO的第一大特性是什么?对,封装。我们可以把ResultSet的处理过程封装起来。
    具体方法如下:

    首先我们需要为每一次查询请求定义一个Query接口。接口里面定义两个方法:
    interface Query{
          public String getSQL();                                 // 获得这个查询的SQL
          public Object doProcess(ResultSet rs);     // 封装了对ResultSet的处理过程
    }

    下面轮到我们传说中的doQuery函数出场了:
    class DBTool{
         public static Object doQuery (Query query){
              Connection conn = ConnectionPool.getConnection();
              Statement stmt = conn.createStatement();

               String SQL = query.getSQL();                // 获得这次查询的SQL
               ResultSet rs = stmt.exeQuery(SQL);      // 在这里创建了ResultSet实例
               Object o = query.doProcess(rs);            // 完成对ResultSet的处理

               rs.close();           // 这里关闭ResultSet就不会有任何问题了 :)
               stmt.close();
               ConnectionPool.release(conn);

               return o;
          }

    }
    为了表达清楚,上面是一个简单的框架,省略了try – catch 以及一些容错判断的部分。

    万事具备了,怎么使用呢?难道每次查询还要写一个实现了Query的类吗?
    当然不用!要不然岂不是多此一举。还记得Swing里面处理Listener的方法吗。我们可以用匿名内部类来实现:

       Object o = DBTool. doQuery (
            new Query(){        // 一个实现了Query接口的匿名内部类
                public String getSQL(){
                    String SQL = "SELECT name, pass FROM table WHERE id < '"+ id +"'";
                    return SQL;
                }

               public Object doProcess(ResultSet rs){
                    Vector vector = new vector();
                    User user = null;
                    while(rs.next()) {
                        user = new User ();
                        user.setName (rs.getString ("name"));
                        user.setPass (rs.getString("pass"));
                        vector.add(user);
                    }
                    return vector;
               }

           } // End new Query()

       );

       // 虽然有可能 抛出 ClassCastException 但这里 返回值的 类型 应该是明确的
       Vector vector = (Vector)o; 

    代码中只剩下我们所关心的构造SQL和处理ResultSet的部分,是不是清爽多了:)


小结:
    doQuery里采用了一个“Don’t call us, we will call you”的模版方法模式,这样就可以把我们所关心的代码部分独立出来了。

同样,我们还可以DBTool实现doUpdate()doExecute()这两个方法,由于不牵涉到ResultSet,所以实现起来要简单得多。这里就不多说了。

这样我们用一个DBTool来帮助我们简化所有的数据库操作,省去了大量重复的代码,快哉快哉 :)

 

 

.蓝山咖啡.  
2004.8.21 夜

 

原创粉丝点击