對象關係映射(Object Relational Mapping,簡稱ORM)是一種為了解決面向對象與關係數據庫存在的互不匹配的現象的技術。簡單的說,ORM是通過使用描述對象和數據庫之間映射的元數據,將程序中的對象自動持久化到關係數據庫中。本質上就是將數據從一種形式轉換到另外一種形式。
ORM提供了所有SQL語句的生成,代碼人員遠離了數據庫概念。從一個概念需求(例如一個HQL)映射為一個SQL語句,並不需要什麼代價,連1%的性能損失都沒有。真正的性能損失在映射過程中,更具體地講,是在對象實例化的過程中。
目前PHP開源比較有名的ORM有以下幾個:
1、Propel
Propel是一個適用於PHP5的ORM映射(Object Relational Mapping)框架,它基於Apache Torque提供對象持久層支持。它通過XML格式的模式定義文件和相應的配置文件來生成SQL和類,它允許你使用對象代替SQL來讀寫數據庫表中的記錄。Propel提供一個生成器來為你的數據模型創建SQL定義文件和PHP類。開發者也可以十分簡單的定制生成的類,我們還可以通過XML, PHP類和Phing構建工具把Propel集成到已有的應用開發框架中去.例如PHP框架symfony的1.2以前的版本就是默認使用了精簡版的Propel作為默認ORM框架。
官方網站:http://www.propelorm.org/
2、Doctrine
Doctrine是一個PHP的ORM框架,它必須運行在>=php5.2.3版本上,它是一個功能強大的數據抽象層。
它的一個主要特徵就是使用面向對象的方式來實現數據庫查詢的封轉,它底層通過一個類似Hibernate HQL的DQL的查詢語句進行數據庫查詢,這使得開發的靈活性更強,大大減小了重複代碼。相比Propel,Doctrine的優點在於它支持支持全文檢索,Doctrine的文檔一直就比Propel要全面豐富,社區更活躍,而且使用起來更加自然、更易閱讀、更接近原生SQL。性能方面也略微優於Propel。同樣你也可以可以很方便的把Doctrine集成到現有的應用框架中去,比如PHP框架symfony的1.3以後的版本將Doctrine作為默認的ORM框架,同時也可以將Doctrine和Codeigniter整合起來。
官方網站: http://www.doctrine-project.org/
3、EZPDO
EZPDO是一個十分輕量級的PHP ORM框架。EZPDO的作者的本意旨在降低複雜的ORM學習曲線,盡可能在ORM的運行效率和功能之間做一個平衡點,它是我至今用過的最簡單的ORM框架,我目前還想將它集成到我的CoolPHP SDK中來,而且運行效率相當不錯,功能也基本能滿足需求,只不過ESPDO的更新比較緩慢。
官方網站:http://www.ezpdo.net/blog/?p=2
4、RedBean
RedBean是一個易於使用,輕量級PHP ORM 框架,提供對MySQL, SQLite&PostgreSQL的支持。RedBean架構非常靈活,核心也非常簡約,開發者可以很方便的通過插件來擴展功能。
官方網站:http://www.redbeanphp.com/
5、其他
國內的fleaphp開發框架基於TableDataGateway實現ORM實現;Zend Framework除了提供對SQL 語句的封裝以外,也同樣實現了TableGateway、TableRowSet、TableRow的實現;還有一些類似Rails的ActiveRecord實現的解決方案。
總的來說,一般ORM框架對付簡單的應用系統來說都能滿足基本需求,可以大大降低開發難度,提高開發效率,但是它在SQL優化方面,肯定是比純SQL語言要差一些,對複雜關聯、SQL內嵌表達式的處理可能不是很理想。也許這主要是由於PHP本身對象持久化的問題,導致ORM效率過低,普遍比純SQL要慢。但是這些都是有辦法解決的,最基本的解決性能的方案,我們可以通過緩存來提高效率,Hibernate來說,雖然配置比較繁雜,但是它通過靈活的使用二級緩存和查詢緩存極大的緩解數據庫的查詢壓力,極大的提升了系統的性能。
如果你想自己實現一個PHP的ORM,下面的可以參考下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
<?php abstract class Model{ protected $pk = 'id'; protected $_ID = null; protected $_tableName; protected $_arRelationMap; protected $_modifyMap; protected $is_load = false; protected $_blForDeletion; protected $_DB; public function __consturct($id = null){ $this->_DB = mysql_connect('127.0.0.1','root','') ; $this->_tableName = $this->getTableName(); $this->_arRelationMap = $this->getRelationMap(); if(isset($id))$this->_ID = $id; } abstract protected function getTableName(); abstract protected function getRelationMap(); public function Load(){ if(isset($this->_ID)){ $sql = "SELECT "; foreach($this->_arRelationMap as $k => $v){ $sql .= '`'.$k.'`,'; } $sql .= substr($sql,0,strlen($sql)-1); $sql .= "FROM ".$this->_tableName." WHERE ".$this->pk." = ".$this->_ID; $result =$this->_DB->mysql_query($sql); foreach($result[0] as $k1 => $v1){ $member = $this->_arRelationMap[$key]; if(property_exists($this,$member)){ if(is_numeric($member)){ eval('$this->'.$member.' = '.$value.';'); }else{ eval('$this->'.$member.' = "'.$value.'";'); } } } } $this->is_load = true; } public function __call($method,$param){ $type = substr($method,0,3); $member = substr($method,3); switch($type){ case 'get': return $this->getMember($member); break; case 'set': return $this->setMember($member,$param[0]); } return false; } public function setMember($key){ if(property_exists($this,$key)){ if(is_numeric($val)){ eval('$this->'.$key.' = '.$val.';'); }else{ eval('$this->'.$key.' = "'.$val.'";'); } $this->_modifyMap[$key] = 1; }else{ return false; } } public function getMember($key,$val){ if(!$this->is_load){ $this->Load(); } if(property_exists($this,$key)){ eval('$res = $this->'.$key.';' ); return $this->$key; } return false; } public function save(){ if(isset($this->_ID)){ $sql = "UPDATE ".$this->_tableName." SET "; foreach($this->arRelationMap as $k2 => $v2){ if(array_key_exists( $k2, $this->_modifyMap)){ eval( '$val = $this->'.$v2.';'); $sql_update .= $v2." = ".$val; } } $sql .= substr($sql_update,0,strlen($sql_update)); $sql .= 'WHERE '.$this->pk.' = '.$this->_ID; }else{ $sql = "INSERT INTO ".$this->_tableName." ("; foreach($this->arRelationMap as $k3 => $v3){ if(array_key_exists( $k3,$this->_modifyMap)){ eval('$val = $this->'.$v3.';'); $field .= "`".$v3."`,"; $values .= $val; } } $fields = substr($field,0,strlen($field)-1); $vals = substr($values,0,strlen($values)-1); $sql .= $fields." ) VALUES (".$vals.")"; } echo $sql; //$this->_DB->query($sql); } public function __destory(){ if(isset($this->ID)){ $sql = "DELETE FROM ".$this->_tableName." WHERE ".$this->pk." = ".$this->_ID; // $this->_DB_query($sql); } } } class User extends Model{ protected function getTableName(){ return "test_user"; } protected function getRelationMap(){ return array( 'id' => USER_ID, 'user_name'=> USER_NAME, 'user_age' => USER_AGE ); } public function getDB(){ return $this->_DB; } } $UserIns = new User(); print_r($UserIns); ?> |