<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## thinkphp5.0數據庫與模型的研究 **last update: 2022-11-03 15:08:22** ---- [TOC=3,8] ---- ### getLastInsID 問題 同一個連接插入后如果不馬上執行 `$this->getQuery()->getLastInsID($sequence)`,哪怕接下來執行一條 select 也會影響 `getLastInsID` 的值(變成0了)。 >[tip] **特別是持久連接時要注意**,所以多個持久連接時要使用 `\PDO::ATTR_PERSISTENT = 'string'` 字符串的方式區分不同的持久連接,不然有問題,參見: `app\smartpark\model\v1\DbActLog` 和 `think\log\driver\Socket` (fpm 下 由于 日志保存是最后執行的,所以沒出現過這個問題,不過如果強制寫入日志時就會有問題了) >[tip] **插入時如果指定了 id 主鍵的值,那么 getLastInsID 就獲取不到插入的id,即 getLastInsID 的意思是獲取 mysql 最后自動生成的id,而不是應用自主寫入的。** ~~~ mysql> INSERT INTO `pt_parking_log` (`id` , `camera_id`) VALUES (1 , 1 ); Query OK, 1 row affected mysql> SELECT LAST_INSERT_ID(); +------------------+ | LAST_INSERT_ID() | +------------------+ | 0 | +------------------+ 1 row in set mysql> INSERT INTO `pt_parking_log` (`camera_id`) VALUES (1); Query OK, 1 row affected mysql> SELECT LAST_INSERT_ID(); +------------------+ | LAST_INSERT_ID() | +------------------+ | 2 | +------------------+ 1 row in set mysql> INSERT INTO `pt_parking_log` (`id` , `camera_id`) VALUES (0 , 1 ); Query OK, 1 row affected mysql> SELECT LAST_INSERT_ID(); +------------------+ | LAST_INSERT_ID() | +------------------+ | 3 | +------------------+ 1 row in set ~~~ ---- ### PDO 連接復用 insertId 錯亂 問題 本文記錄解決 PDO 底層共用連接的導致 getLastInsID 錯誤的問題 [PHP: Connections and Connection management - Manual](https://www.php.net/manual/zh/pdo.connections.php ) >[tip] PDO::ATTR_PERSISTENT選項的值轉換為bool(啟用/禁用持久連接),**除非它是非數字字符串,在這種情況下,它允許使用多個持久連接池。** 如果不同的連接使用不兼容的設置,這很有用 注意:即使 tp 層連接類 不是單例了,但是底層pdo 還是會復用,所以要設置 \PDO::ATTR_PERSISTENT 名稱(字符串)來區分。 ```php <?php namespace app\smartpark\model\v1; class DbActLog extends BModel { protected $table = 'sp_db_act_log'; public function __construct($data = []) { // 避免和主 db 共用連接 $connection = config('database'); $connection['debug'] = false; $connection['query'] = \think\db\Query::class; // 不能使用同一個 主持久連接,否則 PDO 底層 會復用連接 // https://www.php.net/manual/zh/pdo.connections.php $connection['params'][\PDO::ATTR_PERSISTENT] = 'db_act_log'; $this->connection = $connection; parent::__construct($data); } } ``` ---- ### 源碼分析 ~~~php think\Validate protected $rule = [] protected $message = [] protected $field = [] protected $scene = [] $this->batch()-> // 定義場景或選擇使用場景 $this->scene($name, $fields = null)-> hasScene($name) check($data) getError() think\Model // 設置驗證規則 public $this->validate()-> // 自動驗證數據 注意這是受保護的,不能外部主動調用 // 注意,驗證通過的話,模型的驗證規則會被重置位空:$this->validate = null protected validateData() 只在 save/saveAll 中使用了 public save() // insert/update 操作 public saveAll() public getError() // Db::connect() 創建單例的 think\\db\\connector\Mysql [連接器對象] // think\\db\\connector->connect() 單例的 new PDO 保存在 [連接器對象] $this->links[$linkNum] 中 // \\think\\db\\Query 查詢器對象 保存在 think\\Model::$links(即一個模型類一個查詢器單例對象) 中(new 查詢器對象時 傳入了連接實例 和當前模型對象),連接實例 是單例的 保存在 think\\Db::$instance // 使用 db('name') 時,先單例 [連接器對象] connector (think\\Db::$instance),然后在 [連接器對象] 上執行 name()方法,實際是在 一個臨時的、新的查詢器上 (new 查詢器對象時 傳入了當前連接實例) 上執行,即 每次 db() 調用都會 new 新的 查詢器對象 ,但 [連接器對象] 是單例的。 // 注意 new \\think\\db\\Query 時,如果沒有傳入 連接器對象,則會 Db::connect([], true) 強制創建新 [連接器對象] public getQuery() // 獲取當前模型的查詢對象 where 等鏈式操作都在查詢器對象上 // 注意 new \\think\\db\\Query 時,如果沒有傳入 連接器對象,則會 Db::connect([], true); 強制創建新 [連接器對象] 查詢器上的操作 最終都是通過調用 其屬性 [連接器對象] 完成的:$this->connection->execute () 系統獲取 [連接器對象] 都是通過 think\\Db::connect() 連接配置生成key 保持單例 (think\\Db::$instance),數據庫 PDO 連接 是惰性的,只有當 [連接器對象] 上有查詢時才會真正連接,PDO連接 在 [連接器對象] 的 $this->links[$linkNum] 屬性上,所以通常 系統中只有一個 [連接器對象] ,也只有 一個實際的數據庫連接。 pdo sql 最終執行都是在連接器上,連接器上本身也有一些直接使用的sql執行方法 query/execute,如果調用的方法不存在 如 name()方法時,就會創建查詢器 ($this->getQuery())在查詢器上調用這個方法,注意這個創建的查詢器對象是一次性的,沒有在連接器對象上保存起來,所以 think\\Db::name('a') , think\\Db::name('a') 會返回兩個獨立的查詢器對象 。 PDO 對象 在連接器對象上,連接器對象基本是單例的,不管在不同的模型上或是直接使用,所以 結果集的處理時要注意,即使在不同的模型上,因為共用連接,游標等,所以結果集處理時要注意,要防止獲取到連接上上一次的查詢結果集。 模型 每次自動驗證后會清空 $this->validate 所以下次調用時,需要再次設置驗證規則 ->validate(true),傳入 true 則使用定義的 與當前模型名稱同名的驗證器類,tp 這里待優化,只記錄了當前模型名稱,沒有記錄完整類名,如果定位到與當前模塊同名且同模塊的驗證器類。這就導致 如果在 index 模塊 使用 admin 模塊的模型,驗證時使用的還是 index 模塊的 驗證器類,除非 傳入 $this->validate('admin/User') 才行 ~~~ ---- ### 總結 #### 連接器對象: PDO think\\db\\connector\Mysql extends think\\db\\connector $this->linkID 屬性:PDO 數據庫連接對象 單例在 `think\\Db::$instance` 上 (key 數據庫配置MD5) think\\Db::connect() 獲取單例連接器對象(可傳入強制連接,此時會重新實例化連接器對象) think\\Db 調用不存在靜態方法時,其實是調用 其連接器的方法 連接器上方法不存在時 就調用查詢器上的方法(查詢器對象是臨時 new 的沒有單例保存,創建查詢器對象時傳入了自身 $this) `$this->links[$linkNum]` = new PDO(); 連接器上每個連接序號的pdo對象是單例的,所以 **一個數據庫連接在一個連接器對象上只會有一次,連接序號參數是因為主從多庫的情況,此時一個連接器對象上可能有多個數據庫的連接,但同樣,每個數據庫在一個連接器對象只有一次連接** ---- #### 查詢器對象: Active Record: Data \\think\\db\\Query $this->connection 屬性:連接器對象 $this->model 屬性:模型對象 查詢器上的sql執行方法,都是調用 連接器對象上的方法: `query() => $this->connection->query() => $connection->linkID->execute()` 查詢器主要是實現sql 解析等鏈式方法,如 where inc 等。最終生成sql 給連接器執行。 在返回結果前,如果 有 $model 屬性,則會 返回 $model 對象,也就是 ActiveRecord 化處理。 ---- #### 模型: Active Record: Object Item self::$links[get_called_class()] 靜態屬性:查詢器對象 (注意是 self 即 think\Model) static::$initialized[get_class($this)] 靜態屬性:查詢器對象 (注意是 static 即 實際調用的 模型類) self::$event[get_called_class()] 靜態屬性:每個模型上有各自的事件 ```php public function __call($method, $args) { $query = $this->db(true, false); ... } public static function __callStatic($method, $args) { $model = new static(); $query = $model->db(); ... } protected function initialize() { $class = get_class($this); if (!isset(static::$initialized[$class])) { static::$initialized[$class] = true; static::init(); } } protected static function init() { } ``` 當調用不存在的靜態方法時,如 User::find()、Model::with() ,會 自動 new 當前模型類,并調用其 $this->db(true, true) 方法獲取查詢器對象(強制獲取新 查詢器對象),最終調用的是 查詢器上的 find() 方法,并返回。 當調用 User::get() 時,static::parseQuery() -> self::with() : $query->find() (注意這里 static/self 的差別) 獲取查詢器,最終調用查詢器的find() 方法 返回結果。(新模型對象,新查詢器對象) 查詢器對象保存在 `self::$links[$this->class]` 模型類上,**注意是類而不是對象實例。** 所以:如果**調用不存在的靜態方法** 每次會創建新的查詢器對象 $this->db(true, true),如果 **調用不存在的非靜態方法**,則會有單例效果 $this->db(true, false) 即 **一個模型對象上 都是同一個查詢器對象**。 buildQuery() new 創建查詢器對象時,傳入了 Db::connect($connection) 作為連接器對象參數,和 $this 作為模型對象參數。 ~~~php User::get() $query = static::parseQuery() self::with() ->find() 這里 self::with() 而不是 static::with() 暫時沒研究清楚有什么區別意義。 ~~~ ---- #### 分析 1. 平時直接 Db::name('')->find() 執行sql, 多次操作會創建多次查詢器對象,如果使用 new 模型就能避免(注意不是模型方法靜態調用) 2. 不論直接使用連接器對象,還是使用模型,不論多次調用或實例化,數據庫連接對象只有一個,因為單例在 think\\\\Db::$instance 上了。 3. 使用模型必須要定義自己的模型類,沒有驗證器那那樣的 “獨立驗證” ---- ### 補充知識:延遲靜態綁定(late static binding) 注意 static:: 和 self:: 的區別: 類的靜態屬性全局只有一個,這二者都是將數據保存在類的靜態屬性上,不同的是 static:: 是實際執行的具體類,self:: 是當前類(字面代碼所在的類)。 $this-> 是訪問實際具體對象實例上的屬性。 ---- get_called_class() (獲取靜態方法調用的類名) 和 get_class() 的區別: 知識點:延遲靜態綁定(late static binding),靜態方法和屬性在定義是就綁定了,static:: 關鍵字 可以訪問延遲綁定的靜態屬性和方法。 所以請仔細理解 think\\Model 中 : self:: 當前類,即 think\\Model static:: 后期靜態綁定類,如 User::get() 這種 用戶定義的模型類 $this->class = get_called_class() 用來在每個實際具體模型上實現查詢器對象的單例化 self::$links[$this->class] = 查詢器對象(think\\Model::$links[A/B/C/...]) ---- initialize() 模型初始化(實例化) 每個模型類 實例化為對象時的 構造方法 每次 new User 實例化 都會調用初始化方法,如 每次 User::get(1) 都會 new 新的實際模型對象 ---- static init() 模型初始化 static::$initialized[get_class($this)] = true > 同樣也可以使用靜態`init`方法,需要注意的是`init`只在第一次實例化的時候執行(多次 new 也只會執行一次),并且方法內需要注意靜態調用的規范。 待研究 這里明明應該 使用 static::$initialized 標記就行了啊? ---- self::$event 每個模型上的事件 self::$event[get_called_class()][$event][] = $callback; self::$event[stataic] 相當于事件是注冊到 think\Model[模型定義類(A/B/C/...)] 上。 ---- [PHP: 后期靜態綁定 - Manual](https://www.php.net/manual/zh/language.oop5.late-static-bindings.php) ```php <?php // static,self 分析 class A { static $a = 'a'; public function setA() { self::$a = 'b'; } public function setA2() { static::$a = 'b'; } public function getA() { return self::$a; } public function getA2() { return static::$a; } } class B extends A { // 如果沒有子類繼承重寫, static 設置時會有 self 效果 static $a = 'a_'; } class C extends A { static $a = 'a__'; } $A = new A; $B = new B; $C = new C; ---- echo C::$a; // a__ echo $B->getA(); // a echo $B->getA2(); // a_ $B->setA(); echo $B->getA(); // b echo $B->getA2(); // a_ ---- $B->setA(); // A/B/C 都是一樣的 echo $A->getA(); // b echo $A->getA2(); // b echo $B->getA(); // b echo $B->getA2(); // a_ echo $C->getA(); // b echo $C->getA2(); // a__ echo A::$a; // b echo B::$a; // a_ echo C::$a; // a__ ---- $B->setA2(); echo $A->getA(); // a echo $A->getA2(); // a echo $B->getA(); // a echo $B->getA2(); // b echo $C->getA(); // a echo $C->getA2(); // a__ echo A::$a; // a echo B::$a; // b echo C::$a; // a__ ``` ---- ### 相關資料 [數據庫 · ThinkPHP5.0完全開發手冊 · 看云](http://www.hmoore.net/manual/thinkphp5/118058) [模型 · ThinkPHP5.0完全開發手冊 · 看云](http://www.hmoore.net/manual/thinkphp5/135186) [掌握ThinkPHP5數據庫和模型 · 看云](http://www.hmoore.net/thinkphp/master-database-and-model) [ThinkORM開發指南 · 看云](http://www.hmoore.net/manual/think-orm) [ORM介紹 - 知乎](https://zhuanlan.zhihu.com/p/151373067) [ORM 實例教程 - 阮一峰的網絡日志](http://www.ruanyifeng.com/blog/2019/02/orm-tutorial.html) [淺談MyBatis-Plus學習之ActiveRecord - hjjay - 博客園](https://www.cnblogs.com/jayhou/p/9824232.html) > Active Record(簡稱AR),是一種領域模型模式,特點是一個模型類對應關系型數據庫中的一個表,而模型類的一個實例對應表中的一行記錄。 ---- last update: 2022-04-05 18:12:22
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看