posts - 256, comments - 1319, trackbacks - 41, articles - 8
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

        拐弯抹角了两篇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] 代码参考了《数据访问模式——面向对象应用中的数据库交互》

Feedback

#1楼    回复  引用  查看    

2005-07-26 00:26 by YingShen      
Data Accessor象这样封装的话,
似乎每个DAO object都会创建自己的connection对象,
如果业务层需要跨多个表的事务操作
应该怎么办呢?

#2楼 [楼主]   回复  引用  查看    

2005-07-26 09:08 by FantasySoft      
To YingShen: 您说得很对,每个DAO都会拥有自己的Connection实例。如果涉及到事务操作,我在以前的项目中曾经用到的一个方式为:DAO Object增加返回Connection实例的方法,然后在业务逻辑层中获得并维护几个Connection,显式调用Connection的commit方法来提交,并且自己处理rollback(唉,又回到了原始社会)。

很明显,在处理事务方面,使用这样封装的Data Accessor,还是无法避免在业务逻辑层中使用数据访问的代码。 我想折衷的一个方案就是建立跨域对象的DAO,在这样的DAO中封装具体的事务操作,但这样做则会对开发人员有更高的要求了,他必须对业务逻辑和事务操作都十分精通。

不知道大伙有什么其他的方案呢?

#3楼    回复  引用    

2005-07-27 09:20 by johnsir [未注册用户]
把连接字符串加入Web.config文件就可以了

#4楼 [楼主]   回复  引用  查看    

2005-07-27 09:45 by FantasySoft      
To johnsir:您指的将连接字符串加入Web.config文件中,只是为建立Connection提供了可选的一种方式,与多Connection的事务操作问题没有什么关系哦。

#5楼    回复  引用  查看    

2005-12-06 12:24 by 玉开      
可否用com+的事务呢?


标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2005-09-21 15:00 编辑过
 
另存  打印