mabots' blog

知のレバレッジを最大化せよ (旧はてなダイアリーから移転しました。)

PHP5でSingletonを用いたDAOを作ってDBへのアクセスを合理化する


WEBアプリケーションを作っていて、「SQLをまとめたclass」(下記例ではtestEntity)を作っていたとしよう。そして、そのclassをエンティティごとに切り分けたり、機能系ごとに切り分けていたりしたとき、たとえばトップページなどでありもののいろいろなファイルに分散されたSQLをまとめたclassにはいっている複数のSQLをたたきたいというときに何個もコネクションを発行してしまうときがあるので、DB資源の無駄になってしまう。

そんなときにDBのコネクションは常に一個にキープしたい。といういきにいわゆる、Singletonによる実装ができる。下記が例。(実際はまだ未完成ですよ。select()に実際のSQLを書く。PEAR::DBだとdao::$_dao->_master->query($sql,$hash);とか)

注目したい点として

  • getInstanceはstaticなので、インスタンス化しなくても利用できる
  • Singletonが自分自身を格納するプロパティもstaticなので、インスタンス化しなくても(privateなのでこの場合はクラスのメソッドですが)利用できる

があります。

Singletonクラスのコンストラクタに、実際のマスターとスレーブのコネクションを作るとか(たとえばいくつか登録してあるスレーブから一個選択してとか)してあげるとサブクラスをインスタンス化した時点からすぐに利用できて便利です。

dao.phpではSQLをまとめたclassは、スーパークラスとしてSingletonによるクラスdaoを指定し、このシングルトンクラスでDBコネクションを張る。

class testEntity extends dao {
	private $_dbaccess = null;

	public function __construct () {
		$this->_dbaccess = dao::getInstance();
	}


	public function select () {
		//必ずslaveかmasterか選ぶこと!!!!
		$rs = dao::$_dao->_slave->query($sql);
		return "hello world1";
	}
}


class test2Entity extends dao {
	private $_dbaccess = null;

	public function __construct () {
		$this->_dbaccess = dao::getInstance();
	}
	
	public function insert () {
		//必ずslaveかmasterか選ぶこと!!!!
		$rs = dao::$_dao->_master->query($sql,$hash);
		return "hello world2";

	}

}

class dao extends DB { //PEAR::DBを継承するとかで

	protected static $_dao = null;
	protected static $_master = null;
	protected static $_slave = null;
	
	private function __construct() {
		//複数のスレーブコネクションからひとつを選ぶなどして$this->_slaveにアサインするのもいいだろう。以下はPEAR::DB
		echo "Instance Created. Creating connection!";
		$this->_master = DB::connect($master_dsn);
		$this->_slave = DB::connect($slave_dsn);

	}

	public static function getInstance() {
		//自分があるかどうかstaticをみて確認する
		if (dao::$_dao == null) {
			dao::$_dao = new dao();
		} else {
			echo "Instance Already Exists!!";
		}
		return dao::$_dao;
	}

}


利用する側では、こんなかんじでバンバンSQLをまとめたclassをNewできる。

require_once('dao.php');

	$a =& new testEntity();
	echo $a->select();
	$b =& new test2Entity();
	echo $b->insert();


そうすると、下記のような出力を得る。(上の例では動かないですので、DBまわりはちゃんと実装するか省略してください)

Instance Created.Creating connection!
hello world1
Instance Already Exists!!
hello world2


この場合どんなに*EntityをNewしても1pageで実際にコネクションは1個しか作られないのがオツ。