代码改变世界

关于数据访问模式(三)—— Data Accessor模式

2005-07-25 18:34 FantasySoft 阅读(...) 评论(...) 编辑 收藏

        拐弯抹角了两篇Post,说了一下重要性和很常见的一个词汇:Metadata,终于都到言归正传的时候了。今天我们先来看看数据访问模式当中使用得很频繁的一种模式——Data Accessor(也称为DAO,数据访问器)。
        不管你是用着ADO.NET还是JDBC,你都可以直接使用SQL(在ADO.NET中使用SqlCommand,JDBC则是使用Statement)去访问数据库。而且,在使用SQL之前,你还得建立与数据库的连接(Connection)。于是,像这些涉及数据访问的代码就会散布在应用程序当中,使得程序维护或是性能改善都难以实施。举个很简单的例子,如果在一个项目中,每个程序员都要自己去建立与数据库的连接,那么当你希望实现一个连接池(Connection Pool)以提高性能的时候,散布在应用程序各处的建立连接的代码就会令你疲于奔命。因为在这种紧密耦合的情况下,连接池的使用改变了建立连接的方式,从而导致大量的代码改动。为了能够让应用程序更具灵活性,我们通常都会采用一些策略来降低应用程序与数据库访问细节之间的耦合度。Data Accessor就是这些解耦合的策略相当重要的一条。
        Data Accessor的实质就是封装了对数据库访问的细节,仅对应用程序公开逻辑操作。Data Accessor封装细节的程度也就决定了其实现的复杂程度。最简单的一种Data Accessor实现就是由一个Support类提供通用的获得连接的方法,然后进行数据操作的类都必须扩展这个Support类。具体代码如下所示:

public class DAOSupport {
    
public final Connection getConnection() 
{
               
// 获得连接       

     }

    
public final void releaseConnection(Connection conn) {      
               
// 释放连接

     }

}
 

public class UserDAO extends DAOSupport 
{
    
private Connection conn =
 getConnection();
        
}

在以上的代码中,DAOSupport+UserDAO类就可以看作是一个Data Accessor,虽然仅仅是对获得和释放Connection的细节进行了封装,但是这样简单的分离都会令整个开发过程受益匪浅。
        上面的例子虽然实现了最简单的Data Accessor,但是事实上这样的Data Accessor还是太简陋了(原始社会的产品),毕竟你在UserDAO中操作数据表的话,还是得写SQL。好了,为了让这个Data Accessor更加名副其实,我们还要做进一步的抽象。通常对数据库表进行的操作不外乎CRUD,所以我们就可以在DAOSupport中增加相应的四个方法[1]

public List read(String table, String[] column) throws SQLException;
public void
 insert(String table, List rows) throws SQLException;
public void
 update(String table, Row selectedRow, Row updateRow) throws SQLException;
public void
 delete(String table, Row selectedRow) throws SQLException;

 其中Row是一个辅助类,用于表示数据库中的一行中的所有列或者某几列数据,可以通过一个HashMap来实现。上面定义的几个方法,实质上是要在方法体中实现SQL的自动构造,具体的实现就不在这里赘述了,有兴趣的朋友可以参考《数据访问模式——面向对象应用中的数据库交互》一书。 在增加了这四个方法之后,UserDAO中涉及数据访问的代码都可以通过调用这四个方法来完成了,完全脱离了具体的数据访问细节。当你为了优化查询的方法而修改read方法的时候,修改就会影响到所有的*DAO类,一处修改,处处受益。
        虽然我们通过抽象而建立起来的四个方法可以让我们逃避了SQL的困扰,但并非一劳永逸。细心的你一定会发现,read方法的参数仅是表名和列名,这样返回的纪录集将会是数据表中的所有行,因此,我们还是得增加一个参数,使得read方法更加实用。于是read方法就变成了这样:

public List read(String table, String[] column, HashMap conditions) throws SQLException;

在增加了conditions这样参数以后,read方法可以根据条件的界定获得相应的某一行了。功能增加了,随之而来的就是方法复杂度的增加,抽象过程也显得愈加困难。总之,Data Accessor最关键的在于逻辑操作的抽象,复杂度与功能之间的均衡。

[1] 代码参考了《数据访问模式——面向对象应用中的数据库交互》