但是,PDO还有一个更重要的问题没有解决,那就是对数据集的抽象。
无论是ADO还是JDBC,除了提供统一的数据库访问接口以外,也提供了对数据集的抽象。也就是说,在通过ADO/JDBC取回数据集结果以后,这些数据集以统一的格式被存放在RecordSet/RowSet对象中,业务逻辑代码只需要与数据集对象进行交互即可。对数据集进行抽象的直接后果,是彻底地分离了业务逻辑层和数据库访问层的代码,并且也在某种程度上起到了OR Mapping的效果。
自从ADO.NET出现后,数据集的抽象又有了一次不小的进步。和ADO相比,ADO.NET中的DataTable/DataSet类的主要新特性如下:
 非连接性。在传统的ADO模型中,数据集需要占用一个数据库连接,直到所有工作完成。一旦连接被关闭,数据集的内容也就失效了。ADO.NET中的数据集是非连接的,也就是说,当连接被关闭后,数据集中的内容仍然保存。这种非连接性带来的直接后果是,数据库连接可以被最大限度地利用,因为一旦工作完成就可以将连接返回到数据库连接池中。(ADO也支持非连接的数据集,但是需要程序员自己实现,而ADO.NET的数据集在本质上就是非连接的。)
 自描述性。ADO.NET中的数据集是完全自我描述的,而且具有完备的metadata,其内容不但可以从任何特定的数据库生成,而且可以由代码动态生成。DataSet可以跟踪数据的变化,并完成相应的操作。
 互操作性。由于非连接性和自描述性,ADO.NET中的数据集可以非常方便地在网络之间进行传输。DataSet可以序列化/反序列化为XML或其他特定的格式。这样,DataSet不但可以用于同一平台的分布式网络环境,而且可以用于异构网络环境。
Java从J2SE 5.0开始内置了CachedRowSet,其原理和ADO.NET的数据集类似。Borland开发的用于取代BDE的新一代数据库引擎dbExpress,其改进也与此类似。
与之对比,PHP中对数据集的支持显得非常原始。无论是传统的API还是PDO,取回的数据仅仅表现为数组,并且没有任何缓存机制。这意味着,在所有需要访问数据集的地方,都必须频繁地使用直接访问数据库的API。下面是一个使用mysql API的例子:
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
mysql_select_db("justtest");
$result = mysql_query("SELECT id, username FROM userinfo ORDER BY ID");
while ($row = mysql_fetch_array($result)) {
echo $row['id']." | ".$row['username']."<br/>";
}
mysql_close($link);
这样做的后果是业务逻辑和访问数据库的代码无法分离,在规模较大的系统中尤其严重。
就作者所知,PHP官方没有提供支持抽象数据集的计划。但是,自己实现这样一个数据集并不是一件难事。作者参照ADO.NET的架构,使用纯PHP代码编写了一个规模非常小的数据抽象类库,姑且称之为MyPDO。MyPDO大约有1300行代码,在几个真实项目中工作得很好。由于篇幅所限,本文不列出它的所有代码,仅仅给出几个最主要的类的描述:
 DataAdapter接口:定义了所有与数据库操作相关的方法。
 ConceptDataAdapter类:实现了DataAdapter,封装了访问特定数据库的代码。如MySqlDataAdapter。
 DataSet类:实现了自描述的抽象数据集,与具体数据库无关。可以自我跟踪数据的变化。
 SqlCommandBuilder类:可以跟据DataSet类的内容,自动实现Insert、Update、Delete等操作。
下面是MyPDO的一个典型应用:
$Conn = new MySqlDataConnection(new ConnectionInfo("localhost", "mysql_username", "mysql_password", "justtest", "gbk"));
$Da = $Conn->GetDataAdapter();
$Da->SetSqlString("SELECT id, username FROM userinfo ORDER BY ID");
$Ds = new DataSet();
$Da->Fill($Ds);
$Conn->Disconnect(); // 关闭数据库连接,但$Ds仍然保存数据内容
echo $Ds; // 调用DataSet的__tostring()方法,格式化输出内容
通过替换MySqlDataConnection,可以以最小的成本实现不同数据库之间的切换。
由于上文中讨论过的PHP的工作模型,通过MyPDO实现的缓存在性能上获得的好处有限。但是,在采用Memcached的解决方案中,MyPDO还是能够带来很大的便利。因为只有基于非连接方式的数据集,才可能在Memcached这样的内存池中被缓存。
另外,由于MyPDO中的DataSet是自描述的,内置了WriteToXml和ReadFromXml方式,它无需程序员编码就可以保存为XML或从XML中还原,在网络上甚至异构平台之间进行传输和识别。在电子商务领域中,这个特性是非常有用的。
查看本文来源