数据库连接类的设计
网站系统中的数据是存放在数据库中,所以就无法避免地要对数据库进行频繁的读写操作。虽然 PHP 提供了很好的方法,但每次都得写一长串也是个麻烦,所以设计了一个易用的读写数据库类别。
一、利用单例模式(Singleton)创建数据库连接类
数据库连接对象通常会在整个项目中被共享使用,没有必要在每一次使用时都实例化一个对象,不仅低效而且浪费资源。因此采用单例模式的类来确保它在整个应用系统中是唯一的。
单例模式的类通常是采用静态的类方法 getInstance() 来实现的,这个静态方法只返回一个该类的唯一实例。在第一次调用此方法时,该方法将创建一个实例,存放在一个私有的静态变量中,并返回该实例。在下一次调用时,将不再创建新的实例,而是返回第一次所创建的实例。
采用单例模式的类的构造函数通常设为私有,以便防止直接实例化该类而创建新的实例。
Class DBConnect
{
private static $Instance = null;
public static function getInstance() {
if( !isset(self::$Instance) ) {
self::$Instance = new self();
}
return self::$Instance;
}
private function __construct() { … }
private function __clone() {}
}
?>
二、利用简单工厂模式(Simple Factory)取得数据库连接类的实例
在一个应用系统中,虽然一开始就会决定采用何种数据库系统,但也不是不可能更换,所以加入简单工厂模式的设计,可以降低将来更换后修改的工作量。
首先定义一个基楚的数据库连接接口:
interface IDBHelper
{
public static function getInstance();
public function close();
public function getOne($SQL, $aAry);
public function query($SQL, $aAry=null, $bAll=false);
public function execute($SQL, $aAry);
}
?>
然后针对不同的数据库设计相对应的类,令其继承自 IDBConnect 接口,例如:
class MySqlHelper implements IDBHelper { … }
class SqlServerHelper implements IDBHelper { … }
class OracleHelper implements IDBHelper { … }
最后设计一个简单工厂模式的 DBFactory 类依不同状况来取得数据库实例。
class DBFactory
{
Public static function create() {
return DBFactory::createBy(‘MySQL’);
}
public static function createBy($szType) {
switch($szType) {
case 'MySQL' :
return MySqlHelper::getInstance(); break;
default:
die("无法识别的数据库型态:$szType");
exit();
}
}
}
三、实作数据库连接类
从 IDBHelper 接口中可以看出,数据库连接类除了负责连接数据库之外,也得肩负数据库存取的功能,所以有简单的取得一笔数据 getOne()、多笔数据或其他查询 query() 及 execute() 执行新增、修改、删除等动作。
MySqlHelper 类采用 PDO 的方式来操控 MySQL 数据库,其程序代码如下:
/**
* 需要读入一个外部档 password.php。
*/
require_once('IDBHelper.php');
class MySqlHelper implements IDBHelper
{
private static $Instance = null;
protected $aConn = null;
protected $bTransaction = false;
public static function getInstance() {
if( !isset(self::$Instance) ) {
self::$Instance = new self();
}
return self::$Instance;
}
private function __construct() {
require('password.php');
$dsn = "mysql:dbname=$szDBName;host=$szHost;port=$szPort";
try {
$ary = array(PDO::MYSQL_ATTR_INIT_COMMAND =>'SET NAMES \'UTF8\'');
$this->aConn = new PDO($dsn, $szUser, $szPWD, $ary);
} catch (PDOException $e) {
$this->PrintError('Connection failed: '.$e->getMessage());
$this->aConn = null;
exit();
}
}
function __destruct() {
$this->close();
}
public function close() {
if( $this->aConn ) $this->aConn = null;
}
// 以对象的方式传回。
public function getOne( $SQL, $aAry ) {
$sth = $this->aConn->prepare( $SQL );
$sth->execute( $aAry );
return $sth->fetchObject();
}
public function query( $SQL, $aAry=NULL, $bAll=false ) {
$sth = $this->aConn->prepare($szQuery);
$sth->execute($aAry);
if( $bAll )
return $sth->fetchAll(PDO::FETCH_ASSOC);
else
return $sth->fetch(PDO::FETCH_BOTH);
}
// 只执行命令,不传回结果。
public function execute( $SQL, $aAry=NULL ) {
$sth = $this->aConn->prepare( $SQL );
$waiting = true;
while($waiting) {
try {
if( $this->beginTransaction() ) {
$sth->execute($aAry);
$this->commit();
$waiting = false;
}
} catch(PDOException $e) {
$res = stripos($e->getMessage(), 'DATABASE IS LOCKED');
if( $res !== false) {
$this->commit();
} else {
$this->rollback();
$waiting = false;
throw $e;
}
}
}
}
private function beginTransaction() {
if( $this->bTransaction ) {
return false;
} else {
$this->aConn->setAttribute(PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION);
$this->bTransaction = $this->aConn->beginTransaction();
return $this->bTransaction;
}
}
private function commit() {
$this->aConn->commit();
$this->bTransaction = false;
}
private function rollback() {
$this->aConn->rollBack();
$this->bTransaction = false;
}
}
?>
结语
MySqlHelper 类负责对数据库的直接操作,给予什么样的 SQL 命令,就会依指示办事。这里只实作出 MySqlHelper 的部份,至于 SqlServerHelper 及 OrcalHelper,因我个人没用到,所以就没实作过。
参考资料:
《PHP 从入门到精通》 陈超等编着 化学工业出版社