陸續整理數據庫和模型相關的常見問題(保持更新~)
- 內置支持的數據庫有哪些?
- Db類封裝的方法看起來很簡單,是如何實現調用的?
- Db類的內部是什么實現原理?
- 模型和Db類的區別主要是什么?
table
和name
方法的區別是什么?- 模型的
get
和all
方法可以支持條件查詢么? - 模型的
get
和all
方法可以支持排序等鏈式操作么? - 閉包查詢如何傳入變量
- 在模型里面怎么限制查詢字段
- 5.0還有數據表字段緩存么?
- 獲取器和修改器方法名的規范是什么?
- 獲取器是在什么時候觸發?
- 修改器是在什么時候觸發?
- 為什么定義的修改器會執行兩次
- 如何使用視圖模型?
- 設置主從分離后,如何切換到主庫進行查詢操作
- 如何查詢一個字段值為NULL或者NOT NULL的數據?
- 如何直接使用字符串條件進行查詢?
- 如何切換數據庫連接
- 模型中如何使用事務
- Db類如何使用軟刪除功能
內置支持的數據庫有哪些?
5.0
內置支持的數據庫包括Mysql
、Sqlite
、Pgsql
和SqlServer
,另外還提供了Oracle
和MongoDb
的擴展驅動。
Db類封裝的方法看起來很簡單,是如何實現調用的?
Db類只是一個數據庫操作的入口類,數據庫的查詢方法都是由Query類實現的,調用Db類的靜態方法會自動實現Query類方法的動態調用。并且Db類是一個工廠類,針對不同的數據庫驅動實現了統一的封裝。
Db類的內部是什么實現原理?
Db類可以看成是數據庫抽象訪問層的入口類,抽象訪問層本身包含了連接器類(負責連接不同的數據庫和執行基礎查詢)、查詢器類(負責各種查詢方法的實現)和生成器類(負責把查詢方法轉換為基礎查詢語句),各司其職完成了跨數據庫的底層操作,我們只需要操作Db類即可完成數據庫抽象訪問層的操作,系統的數據庫抽象訪問層實際上是對PDO的一種擴展和增強,主要的優勢是CURD的增強和查詢事件,PDO本身只有基礎查詢方法。
模型和Db類的區別主要是什么?
Db類(其實就是數據庫抽象訪問層)負責數據和查詢本身,而模型類側重于業務邏輯和數據處理。Db類僅僅是單純的進行操作存取操作,主要使用的是數組類型,而模型操作的數據主要是模型的對象實例,更加面向對象化和直觀,并且提供了模型關聯操作可以大大簡化業務邏輯的相關處理和數據獲取,模型本身是依賴數據庫抽象訪問層的。
table
和name
方法的區別是什么?
Db類提供了table
和name
兩個方法,table
方法用于指定數據表的完整名稱(包括前綴,而且不會進行大小寫轉換處理),name
方法僅僅用于指定數據表的標識(會把駝峰表名轉換為小寫加下劃線的方式,并且不包含前綴)。
如果你遵循框架的數據表命名規范,并且不使用表前綴的話,這兩個方法是等效的。
模型的get
和all
方法可以支持條件查詢么?
可以,如果模型的get
和all
方法如果傳入一個索引數組,就表示查詢條件,例如:
User::all([
'name' => 'thinkphp',
'id' => ['>', 10],
]);
模型的get
和all
方法可以支持排序等鏈式操作么?
模型類的get和all方法之前不支持Db類的鏈式方法調用,如果你需要使用Db類的鏈式方法,可以使用閉包方式,例如:
User::all(function($query){
$query->field('name,email,id')->order('id');
});
等效于
User::field('name,email,id')->order('id')->select();
但區別是可以支持模型的事件操作。
閉包查詢如何傳入變量
如果要在閉包中傳入外部變量,可以使用use
語法,例如:
User::get(function($query) use($name){
$query->where('name',$name);
});
用類似的方法可以支持傳入多個變量,下面的用法是錯誤的:
User::get(function($query,$name) {
$query->where('name',$name);
});
在模型里面怎么限制查詢字段
如果在模型里面調用的是find
或者select
方法,那么依然可以使用field
方法進行字段限制,模型類的get
和all
方法查詢的話本身不支持field
方法,但可以通過閉包完成(參考上一個問題)。
但并不建議模型查詢的時候指定字段,因為可能會影響模型的獲取器,尤其存在依賴關系的話。如果僅僅是希望不暴露敏感數據,則可以在輸出數據的時候使用hidden
或者visible
方法進行隱藏和指定顯示。
5.0還有數據表字段緩存么?
5.0默認不會生成字段緩存,但提供了數據表字段緩存的命令行指令,你可以在部署上線后執行php think optimize:schema
命令生成字段緩存。
獲取器和修改器方法名的規范是什么?
獲取器和修改器方法的命名規范分別是getFieldNameAttr
和setFieldNameAttr
,其中的FieldName
是數據表字段的駝峰法表示,也就是對應數據表的field_name
字段。
獲取器是在什么時候觸發?
獲取器的作用是對模型的數據對象的(原始)數據做出自動處理,定義了獲取器之后會在下列情況自動觸發:
- 模型的數據對象取值操作(
$model->field_name
); - 模型的序列化輸出操作(
$model->toArray()
或者$model->toJson()
); - 顯式調用
getAttr
方法($this->getAttr('field_name')
);
修改器是在什么時候觸發?
修改器的作用是在寫入數據庫之前對模型數據進行修改處理,一般在顯式賦值(包括單個賦值和批量賦值)的時候會自動觸發,不過有一些修改器是在設置了自動完成后被動觸發,下面是觸發條件。
- 模型對象賦值;
- 調用模型的data方法,并且第二個參數傳入true;
- 調用模型的save方法,并且傳入數據;
- 顯式調用模型的setAttr方法;
- 定義了該字段的自動完成;
為什么定義的修改器會執行兩次
如果定義的修改器字段同時定義了自動完成,并且你也進行了賦值操作,那么就會導致修改器執行兩次。避免的方法是對需要賦值操作的字段不再定義自動完成。典型的場景就是密碼字段使用md5加密保存,如果定義了自動完成,然后同時表單又賦值了,那么可能會被加密兩次導致出錯。
如何使用視圖模型?
ThinkPHP提供了多種方式的視圖查詢的支持,包括:
一、直接在數據庫中創建視圖
可以直接在數據庫中創建視圖,然后使用Db類或者創建模型類進行操作,這種方式的優點是方便,缺點是有些數據庫不支持創建視圖,而且不支持數據寫入操作。
二、使用Db類的view
方法動態創建視圖查詢
這種方式可以動態的創建一個視圖并進行查詢操作,而不依賴數據庫,缺點也是不支持數據寫入。
三、使用聚合模型
可以把聚合模型看成是view方法的模型封裝,而且可以支持寫入操作,缺點是不直觀和配置麻煩,而且聚合模型和原始模型不能同時使用(最新版本已經不再推薦使用)。
四、使用模型關聯
最新版本的模型關聯對一對一關聯改進了很多,包括關聯屬性綁定到當前模型,以及關聯自動寫入功能,這是ThinkPHP5最為推薦的視圖操作方式,相比前面幾種,優勢是操作直觀和支持寫入。
設置主從分離后,如何切換到主庫進行查詢操作
一旦開啟了數據庫的主從分離,默認情況下所有讀操作都是在從庫,而寫操作則是在主庫,如果因為某些情況需要(例如對于實時性要求較高的查詢,寫入后立刻查詢從庫同步還不及時的情況)在主庫進行查詢,我們可以使用
Db::name('user')->where('id',10)->update(['name'=>'thinkphp']);
// 連接到主庫進行查詢操作
Db::name('user)->master(true)->find(10);
如何查詢一個字段值為NULL或者NOT NULL的數據?
5.0.5
版本開始,可以直接使用快捷查詢方法如下:
// 查詢name為NULL的數據
Db::name('user')->whereNull('name')->select();
// 查詢name為NOT NULL的數據
Db::name('user')->whereNotNull('name')->select();
如果要使用OR查詢,可以傳入第二個參數為OR
// 查詢name為thinkphp或者NULL的數據
Db::name('user')->where('name','thinkphp')->whereNull('name','or')->select();
// 查詢name為thinkphp或者NOT NULL的數據
Db::name('user')->where('name','thinkphp')->whereNotNull('name','or')->select();
在5.0.5
版本之前,可以使用下面的方式
// 查詢name為NULL的數據
Db::name('user')->where('name','null')->select();
// 查詢name為NOT NULL的數據
Db::name('user')->where('name','not null')->select();
如何直接使用字符串條件進行查詢?
可以在where方法的第一個參數直接傳入字符串條件,并且可以和其它條件混合使用:
Db::name('user')
->where('( name like :name OR name IS NULL ) AND id > :id', ['name' => '%think%', 'id' => 10])
->where('email', 'like', '%think')
->select();
如何切換數據庫連接
無論在Db類還是模型里面,要切換數據庫都可以直接調用connect
方法,該方法的參數可以是數組或者DNS字符串,以及配置文件中的數據庫連接配置參數名,例如:
Db::connect('db_config')->name('user')->find();
必須在
connect
方法后面調用查詢。
模型中如何使用事務
在模型中使用事務和數據庫中使用事務一樣,
$this->startTrans();
try{
// 添加實現代碼
// ...
// 提交事務
$this->commit();
} catch (\Exception $e) {
// 回滾事務
$this->rollback();
}
但仍然建議直接使用
$this->transaction(function(){
// 添加實現代碼
});
能夠實現事務的自動提交及回滾。
Db類如何使用軟刪除功能
軟刪除功能是模型的特性,Db類默認不支持,不過5.0.8+
版本開始,可以使用useSoftDelete
方法來支持軟刪除操作。
查詢數據的時候不包含軟刪除數據(假設軟刪除字段是delete_time
)。
Db::name('user')->useSoftDelete('delete_time')->select();
上一篇:第九章:性能和安全
下一篇:附錄B:使用MongoDb