Thinkphp 模型和數據庫:數據庫架構基礎

本章我們首先從ThinkPHP5.0的數據庫訪問層架構設計原理開始,然後熟悉下數據庫的配置,並掌握如何進行基礎的查詢操作,並簡單介紹了分佈式、存儲過程及事務,學習內容主要包括:

  • 數據庫架構設計
  • 數據庫配置
  • 如何開始查詢
  • 使用參數綁定
  • 查詢返回值
  • 動態連接數據庫
  • 分佈式支持
  • 存儲過程調用
  • 數據庫事務
  • 總結

數據庫架構設計

使用框架開發應用,一般不需要直接操作數據庫,而是通過框架封裝好的數據庫中間層對數據庫進行操作。這樣的好處主要有兩個:一是簡化數據庫操作,二是做到跨數據庫的一致性。這種設計的中間層通常稱之為數據庫訪問抽象層,簡稱數據訪問層(DAL),ThinkPHP5的數據訪問層是基於PHP內置的PDO對象實現。一般抽象層本身並不直接操作數據庫,而是通過驅動來實現具體的數據庫操作。

ThinkPHP5.0的數據庫設計相比之前版本更加合理,數據訪問層劃分的更細化,把數據訪問對象分成了連接器、查詢器、生成器等多個對象,並通過數據庫訪問入口類統一調用,分工更明確,各司其職,欲知詳情且聽我慢慢道來。

ThinkPHP數據訪問層設計示意圖:

Thinkphp 模型和數據庫:數據庫架構基礎

5.1版本的架構略微進行了一些調整,變成:

Thinkphp 模型和數據庫:數據庫架構基礎

數據庫入口類Db

平常我們的數據庫操作使用的類庫一般都是數據庫的入口類think\\Db。這個類非常的簡單,主要就是一個connect方法,根據數據庫配置參數連接數據庫(注意這裡的連接並非真正的連接數據庫,只是做好了隨時連接的準備工作,只有在實際查詢的時候才會真正去連接數據庫,是一種惰性連接)並獲取到數據庫連接對象的實例。

Db類都是靜態方法調用,但看起來這個類啥都沒實現,那是怎麼操作數據庫的呢,其實就是封裝了數據庫操作方法的靜態調用(利用__callStatic方法),下面是代碼實現:

<code>// 調用驅動類的方法
public static function __callStatic($method, $params)
{
// 自動初始化數據庫
return call_user_func_array([self::connect(), $method], $params);
}

/<code>

理論上來說,框架並不依賴Db類,該類的存在只是為了簡化數據庫抽象層的操作而提供的一個工廠類,否則你就需要單獨實例化不同的數據庫連接類。因此,看似可有可無的Db類就成了數據訪問層實現的點睛之筆了。

所有的數據庫操作都是經過Db類調用,並且Db類是一個靜態類,但Db類自身只有一個公共方法connect。

連接器類Connection

顧名思義,連接類的作用就是連接數據庫,也稱為連接器。我們知道,不同的數據庫的連接方式和參數都是不同的,連接類就是要解決這個差異問題。

數據庫入口類裡面實例化的類其實就是對應數據庫的連接類,連接類的基類是think\\db\\Connection。例如,需要連接Mysql數據庫的話,就必須定義一個Mysql連接類(內置由think\\db\\connector\\Mysql類實現,繼承了think\\db\\Connection類),當然具體的連接類名沒有固定的規範(例如,MongoDb的連接類就是think\\mongo\\Connection)。如果某個數據庫的連接擴展類沒有繼承think\\db\\Connection,那就意味著所有的數據庫底層操作有可能被接管,在個別特殊的數據庫的擴展中就有類似的實現,例如MongoDb數據庫擴展。

數據庫連接都是惰性的,只有最終執行SQL的時候才會進行連接。

連接器是數據訪問層的基礎,基於PHP本身的PDO實現(如果你還不瞭解PDO,請參考PHP官方手冊中PDO部分,不在本書的討論範疇),連接類的主要作用就是連接具體的數據庫,以及完成基本的數據庫底層操作,包括對分佈式、存儲過程和事務的完善處理。而更多的數據操作則交由查詢類完成。

框架內置的連接類包括:

數據庫 連接類 Mysql think\\db\\connector\\Mysql Pgsql think\\db\\connector\\Pgsql Sqlite think\\db\\connector\\Sqlite Sqlsrv think\\db\\connector\\Sqlsrv

如果是僅僅使用原生SQL查詢的話,只需要使用連接類就可以了(通過調用Db類完成)

連接器類的作用小結:

  • 連接數據庫;
  • 獲取數據表和字段信息;
  • 基礎查詢(原生查詢);
  • 事務支持;
  • 分佈式支持;

查詢器類Query

除了基礎的原生查詢可以在連接類完成之外,其它的查詢操作都是調用查詢類的方法,查詢類內完成了數據訪問層最重要的工作,銜接了連接類和生成類,統一了數據庫的查詢用法,所以查詢類是不需要單獨驅動配合的,我們也稱之為查詢器。無論採用什麼數據庫,我們的查詢方式是統一的,因為數據訪問層核心只有一個唯一的查詢類:think\\db\\Query。

Query類封裝了所有的數據庫CURD方法的優雅實現,包括鏈式方法及各種查詢,並自動使用了PDO參數綁定(參數自動綁定是在生成器類解析生成SQL時完成),最大程度地保護你的程序避免受數據庫注入攻擊,查詢操作會調用生成類生成對應數據庫的SQL語句,然後再調用連接類提供的底層原生查詢方法執行最終的數據庫查詢操作。

所有的數據庫查詢都使用了PDO的預處理和參數綁定機制。你所看到的大部分數據庫方法都來自於查詢類而並非Db類,這一點很關鍵,也就是說雖然我們始終使用Db類操作數據庫,而實際上大部分方法都是由查詢器類提供的方法。

生成器類Builder

生成類的作用是接收Query類的所有查詢參數,並負責解析生成對應數據庫的原生SQL語法,然後返回給Query類進行後續的處理(包括交給連接類進行SQL執行和返回結果處理),也稱為(語法)生成器。生成類的作用其實就是解決不同的數據庫查詢語法之間的差異。查詢類實現了統一的查詢接口,而生成類負責數據庫底層的查詢對接。

生成類一般不需要自己調用,而是由查詢類自動調用的。也可以這麼理解,生成類和查詢類是一體的,事實上它們合起來就是通常我們所說的查詢構造器(因為實際的查詢操作還是在連接器中執行的)。

通常每一個數據庫連接類都會對應一個生成類,框架內置的生成類包括:

數據庫 生成類 Mysql think\\db\\builder\\Mysql Pgsql think\\db\\builder\\Pgsql Sqlite think\\db\\builder\\Sqlite Sqlsrv think\\db\\builder\\Sqlsrv

這些生成類都繼承了核心提供的生成器基類think\\db\\Builder,每個生成器類只需要提供差異部分的實現。

數據庫配置

數據庫的配置參數有很大的學問,也是你掌握數據庫操作的基礎,主要用於數據庫的連接以及查詢的相關設置。

數據庫的配置參數用於連接類的架構方法,而由於我們並不直接操作連接類,所以,配置參數主要通過Db類傳入並設置到當前的數據庫連接類。

數據庫配置分為靜態配置動態配置兩種方式,靜態配置是指在數據庫配置文件中進行配置,動態配置是指在Db類或者Query類的connect方法中傳入動態的配置參數。

安裝好ThinkPHP5之後,默認在application目錄下面會有一個database.php文件,這就是應用的數據庫配置文件,如果你的模塊需要單獨的數據庫配置文件,那麼只需要在模塊目錄下面創建一個database.php文件即可,並且只需要定義和應用數據庫配置文件有差異的部分。

數據庫配置文件中配置的是默認的數據庫連接配置,如果你有多個數據庫連接,額外的數據庫連接是在應用配置文件中完成的(參考後面的動態數據庫連接)。

<code>├─application           
│ ├─index
│ │ ├─database.php (模塊)數據庫配置文件
│ │ └─ ...
│ ├─database.php (應用)數據庫配置文件
│ └─ ...

/<code>

我們下面的數據庫配置文件都以應用數據庫配置文件為例說明。

默認的應用數據庫配置文件如下:

<code>return [
// 數據庫類型
'type' => 'mysql',
// 服務器地址
'hostname' => '127.0.0.1',
// 數據庫名
'database' => '',
// 用戶名
'username' => 'root',
// 密碼
'password' => '',
// 端口
'hostport' => '',
// 連接dsn
'dsn' => '',
// 數據庫連接參數
'params' => [],
// 數據庫編碼默認採用utf8

'charset' => 'utf8',
// 數據庫表前綴
'prefix' => '',
// 數據庫調試模式
'debug' => true,
// 數據庫部署方式:0 集中式(單一服務器),1 分佈式(主從服務器)
'deploy' => 0,
// 數據庫讀寫是否分離 主從式有效
'rw_separate' => false,
// 讀寫分離後 主服務器數量
'master_num' => 1,
// 指定從服務器序號
'slave_no' => '',
// 是否嚴格檢查字段是否存在
'fields_strict' => true,
// 數據集返回類型
'resultset_type' => 'array',
// 自動寫入時間戳字段
'auto_timestamp' => false,
// 時間字段取出後的默認時間格式
'datetime_format' => 'Y-m-d H:i:s',
// 是否需要進行SQL性能分析
'sql_explain' => false,
// Builder類
'builder' => '',
// Query類
'query' => '\\\\think\\\\db\\\\Query',
];

/<code>

最關鍵的參數就是下面幾個(其它參數後面會陸續涉及):

參數名 作用 type 數據庫類型或者連接類名 hostname 數據庫服務器地址(一般是IP地址,默認為127.0.0.1) username 數據庫用戶名(默認為root) password 數據庫用戶密碼(默認為空) database 使用的數據庫名稱 charset 數據庫編碼(默認為utf8)

type參數嚴格來說其實配置的是連接類名(而不是數據庫類型),支持命名空間完整定義,不帶命名空間定義的話,默認採用\\think\\db\\connector作為命名空間(內置連接類的命名空間)。你完全可以在應用中擴展自己的數據庫連接類,例如配置為:

<code>// 配置數據庫類型(連接類)為自定義
'type' => '\\app\\db\\Mysql',

/<code>

這樣就可以自己替換或者擴展一些額外的數據庫操作方法。

自定義連接類的時候,請注意設置數據庫配置中的builder參數避免找不到對應生成器類。

ThinkPHP5.0採用PDO來統一操作數據庫,而連接類的最關鍵的作用就是通過配置連接到數據庫,PDO的連接方法參數如下:

PDO::__construct ( 'DSN' ,'用戶名','密碼','連接參數(數組)' )

數據庫的數據源名稱(DSN)是最關鍵的一個參數,連接類負責把數據庫配置參數自動轉換為一個有效的DSN數據源名稱。如果你有特殊的連接語法需求,則可以通過配置數據庫配置文件中的dsn參數來解決,該配置參數的值會直接用於PDO連接,例如:

<code>數據庫支持斷線重連機制(默認關閉),可以設置(V5.0.6+版本僅支持Mysql數據庫,V5.0.9+版本開始支持內置所有數據庫):// 連接dsn
'dsn' => 'mysql:unix_socket=/tmp/mysql.sock;dbname=demo',

/<code>

數據庫支持斷線重連機制(默認關閉),可以設置(V5.0.6+版本僅支持Mysql數據庫,V5.0.9+版本開始支持內置所有數據庫):
// 開啟斷線重連'break_reconnect' => true,
除了DSN數據源名稱,PDO的連接參數也可以單獨設置,每個連接驅動都有自己的連接參數設置,Mysql連接器內置採用的參數包括如下:

<code>PDO::ATTR_CASE              => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,

/<code>

可以在數據庫配置文件中設置params參數,會和內置的連接參數合併,例如:

<code>    // 數據庫連接參數
'params' => [
// 使用長連接
\\PDO::ATTR_PERSISTENT => true,
// 數據表字段統一轉換為小寫
\\PDO::ATTR_CASE => \\PDO::CASE_LOWER,
],

/<code>

常用數據庫連接參數(params)可以參考PHP在線手冊中的以PDO::ATTR_開頭的常量。

如何開始查詢

在開始學習查詢之前,我們首先在demo數據庫中創建一個data測試表。

<code>CREATE TABLE IF NOT EXISTS `data`(
`id` int(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL COMMENT '名稱',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

/<code>

然後設置數據庫配置文件內容為(如果有密碼請自行修改):

<code>return [
// 數據庫類型
'type' => 'mysql',
// 服務器地址
'hostname' => '127.0.0.1',
// 數據庫名
'database' => 'demo',
// 用戶名
'username' => 'root',
// 密碼
'password' => '',
// 開啟數據庫調試
'debug' => true,
];

/<code>

特別注意我們在配置中開啟了debug參數,表示開啟數據庫的調試模型,開啟後會記錄數據庫的連接信息和SQL日誌,數據庫的調試模式和應用的調試模式是兩個不同的概念。

配置完數據庫連接信息後,我們就可以直接使用Db類進行數據庫運行原生SQL操作了,你無需關心數據庫的連接操作,系統會自動使用數據庫配置參數進行數據庫的連接操作。

Db類的方法都是靜態調用(不需要去實例化think\\Db類),Db類的查詢方法有很多(大部分查詢都是使用的查詢構造器),本章內容暫時只講兩個用於原生查詢的方法,包括query(查詢操作)和execute(寫入操作),更多的查詢方法會在查詢構造器章節作出詳細講解。

數據庫查詢的所有示例都需要寫到一個控制器的方法裡面,我們現在假設你已經定義了一個下面的控制器操作方法:

<code>
namespace app\\index\\controller;

use think\\Db;

class Index
{
public function index()
{
// 這裡是數據庫操作的測試代碼
// ...
return;
}
}

/<code>

一般來說並不建議在控制器的操作方法中直接操作數據庫Db類,但由於我們還沒涉及到模型章節的內容,因此,目前的寫法僅為了演示數據庫的示例代碼。

並且在應用配置文件中開啟頁面Trace顯示:
// 應用Trace 'app_trace' => true,
開啟頁面Trace的作用是為了方便我們查看當前請求的SQL語句信息以及執行時間(需要開啟數據庫調試模式後有效)。

然後在index操作方法中添加下面測試代碼:

<code>Db::execute('insert into data (id, name) values (1, "hinkphp")');
Db::query('select * from data where id=1');

/<code>

對數據表的CURD操作,除了select和存儲過程調用使用query方法之外,其它的操作都使用execute方法,這裡就不再一一演示了。


訪問頁面後,顯示空白,點擊右下角的

Thinkphp 模型和數據庫:數據庫架構基礎

就可以打開頁面Trace信息,切換到SQL一欄,可以看到下面的類似信息

Thinkphp 模型和數據庫:數據庫架構基礎

第一條表示數據庫的連接信息(連接消耗時間以及連接的DSN),後面的兩條就表示當前操作執行的SQL語句,由於我們使用的是原生查詢,所以SQL語句和你的代碼裡面的SQL語句是一致的,每條SQL語句最後會顯示該SQL語句的執行消耗時間。

細心的朋友會發現Db類裡面並沒有query和execute方法,其實在調用Db類的方法(connect方法除外)之前,都會先調用connect方法進行數據庫的初始化(前面提過的__callStatic方法),由於connect方法會返回一個數據庫連接類的對象實例(根據配置參數實現了單例),所以Db類調用的query和execute方法其實就是連接器類(Connection)的方法,這一點必須理解,否則你很難理解數據庫的查詢操作。

使用參數綁定

上面的例子是實際開發中其實並不建議,原則上我們在使用原生查詢的時候最好使用參數綁定避免SQL注入,例如:

<code>Db::execute('insert into data (id, name) values (?, ?)',[2,'kancloud']);
Db::query('select * from data where id=?',[2]);/<code>

頁面Trace信息中會顯示實際運行的SQL語句

也支持命名佔位符綁定,例如:

<code>Db::execute('insert into data (id, name) values (:id, :name)',['id'=>3,'name'=>'topthink']);
Db::query('select * from data where id=:id',['id'=>3]);/<code>

參數綁定的變量不需要使用引號

同樣顯示的實際執行SQL如下:

Thinkphp 模型和數據庫:數據庫架構基礎

我們看到查詢語句中的id的值是字符串的,由於參數綁定默認都是使用的字符串,如果需要指定為數字類型,可以使用下面的方式:

<code>Db::execute('insert into data (id, name) values (:id, :name)',['id'=>[4,\\PDO::PARAM_INT],'name'=>'onethink']);
Db::query('select * from data where id=:id',['id'=>[4,\\PDO::PARAM_INT]]);/<code>

這次查看實際的執行SQL會有細微的變化

Thinkphp 模型和數據庫:數據庫架構基礎

PDO命名佔位綁定不支持一個參數多處綁定,下面的用法會報錯:

<code>Db::execute('insert into data (name) values (:name),(:name)',['name'=>'thinkphp']);/<code>
Thinkphp 模型和數據庫:數據庫架構基礎

該錯誤信息表示你的參數綁定參數數量不符。

查詢返回值

使用Db類查詢數據庫的話,query方法的返回值是一個二維數組的數據集,每個元素就是一條記錄,例如:

<code>array (size=1)
0 =>
array (size=5)
'id' => int 8

'name' => string 'thinkphp' (length=8)/<code>

相比query方法,execute方法的返回值就比較單純,一般就是返回影響(包括新增和更新)的記錄數,如果沒有影響任何記錄,則返回值為0,所以千萬不要用布爾值來判斷execute是否執行成功,事實上,在5.0裡面不需要判斷是否成功,因為如果發生錯誤一定會拋出異常。

動態連接數據庫

當你需要使用多個數據庫連接的時候,就需要使用connect方法動態切換到另外一個數據庫連接,假設存在另外一個數據庫test,並且複製data過去更名為test,然後測試下面的示例:

<code>Db::query('select * from data where id = 2');
Db::connect([
// 數據庫類型
'type' => 'mysql',
// 服務器地址
'hostname' => '127.0.0.1',
// 數據庫名
'database' => 'test',
// 用戶名
'username' => 'root',
// 密碼
'password' => '',
// 開啟調試模式
'debug' => true,
])->query('select * from test where id = 1');
Db::query('select * from data where id = 3');/<code>

頁面Trace的顯示信息可以看出來使用了兩次數據庫連接和執行了三次查詢,並且數據庫連接切換並沒有影響默認的查詢(第三個查詢還是使用的默認數據庫配置連接,test數據庫中並不存在data表,如果連接的還是第二個數據庫連接的話肯定會報錯)。

Thinkphp 模型和數據庫:數據庫架構基礎

有時候,我們只需要設置一些基本的數據庫配置參數,可以簡化成一個字符串格式定義(該格式為ThinkPHP使用規範,而不是PDO連接規範,不要和DSN混淆起來):

<code>Db::connect('mysql://[email protected]/demo#utf8')
->query('select * from data where id = 1');/<code>

字符串格式的連接信息規範格式如下:

數據庫類型://用戶名[:用戶密碼]@數據庫服務器地址[:端口]/數據庫名[?參數1=值&參數2=值]#數據庫編碼

Db類的connect方法會返回一個數據庫連接對象實例,相同的連接參數返回的是同一個對象實例,除非你強制重新實例化,例如:

<code>Db::connect([
// 數據庫類型
'type' => 'mysql',
// 服務器地址
'hostname' => '127.0.0.1',
// 數據庫名
'database' => 'demo',
// 用戶名
'username' => 'root',
// 密碼
'password' => '',
],true)->query('select * from data where id = 1');/<code>

這樣,每次調用都會重新實例化數據庫的連接類。

為了便於統一管理,你可以把數據庫配置參數納入配置文件,例如在應用配置文件中添加:

<code>'db_config' =>  [
// 數據庫類型
'type' => 'mysql',
// 服務器地址
'hostname' => '127.0.0.1',
// 數據庫名
'database' => 'demo',
// 用戶名
'username' => 'root',
// 密碼
'password' => '',
],/<code>

或者使用字符串方式定義

<code>'db_config' =>  'mysql://[email protected]/demo',/<code>

上面的db_config配置參數不是在數據庫配置文件中定義,而是在應用配置文件或者模塊配置文件中定義。

然後,使用下面的方式來動態連接獲取切換連接

<code>Db::connect('db_config')
->query('select * from data where id=:id', ['id'=>3]);/<code>

當connect方法傳入的連接參數是字符串並且不包含/等特殊符號的話,表示使用的是預定義數據庫配置參數。

分佈式支持

數據訪問層支持分佈式數據庫,包括讀寫分離,要啟用分佈式數據庫,需要開啟數據庫配置文件中的deploy參數:

<code>return [
// 啟用分佈式數據庫
'deploy' => 1,
// 數據庫類型
'type' => 'mysql',
// 服務器地址
'hostname' => '192.168.1.1,192.168.1.2',
// 數據庫名
'database' => 'demo',
// 數據庫用戶名
'username' => 'root',
// 數據庫密碼
'password' => '',
// 數據庫連接端口
'hostport' => '',

];/<code>

啟用分佈式數據庫後,hostname參數是關鍵,hostname的個數決定了分佈式數據庫的數量,默認情況下第一個地址就是主服務器。

主從服務器支持設置不同的連接參數,包括:

連接參數 username password hostport database dsn charset

如果主從服務器的上述參數一致的話,只需要設置一個,對於不同的參數,可以分別設置,例如:

<code>return [
// 啟用分佈式數據庫
'deploy' => 1,
// 數據庫類型
'type' => 'mysql',
// 服務器地址
'hostname' => '192.168.1.1,192.168.1.2,192.168.1.3',
// 數據庫名
'database' => 'demo',
// 數據庫用戶名
'username' => 'root,slave,slave',
// 數據庫密碼
'password' => '123456',
// 數據庫連接端口
'hostport' => '',
// 數據庫字符集
'charset' => 'utf8',
];/<code>

記住,要麼相同,要麼每個都要設置。

還可以設置分佈式數據庫的讀寫是否分離,默認的情況下讀寫不分離,也就是每臺服務器都可以進行讀寫操作,對於主從式數據庫而言,需要設置讀寫分離,通過下面的設置就可以:

<code>    'rw_separate' => true,/<code>

在讀寫分離的情況下,默認第一個數據庫配置是主服務器的配置信息,負責寫入數據,如果設置了master_num參數,則可以支持多個主服務器寫入(每次隨機連接其中一個主服務器)。其它的地址都是從數據庫,負責讀取數據,數量不限制。每次連接從服務器並且進行讀取操作的時候,系統會隨機進行在從服務器中選擇。同一個數據庫連接的每次請求只會連接一次主服務器和從服務器,如果某次請求的從服務器連接不上,會自動切換到主服務器進行查詢操作。

如果不希望隨機讀取,或者某種情況下其它從服務器暫時不可用,還可以設置slave_no 指定固定服務器進行讀操作,slave_no指定的序號表示hostname中數據庫地址的序號,從0開始。

調用查詢類或者模型的CURD操作的話,系統會自動判斷當前執行的方法是讀操作還是寫操作並自動連接主從服務器,如果你用的是原生SQL,那麼需要注意系統的默認規則: 寫操作必須用數據庫的execute方法,讀操作必須用數據庫的query方法,否則會發生主從讀寫錯亂的情況。

發生下列情況的話,會自動連接主服務器:

  • 使用了數據庫的寫操作方法(execute/insert/update/delete以及衍生方法);
  • 如果調用了數據庫事務方法的話,會自動連接主服務器;
  • 從服務器連接失敗,會自動連接主服務器;
  • 調用了查詢構造器的lock方法;
  • 調用了查詢構造器的master方法

主從數據庫的數據同步工作不在框架實現,需要數據庫考慮自身的同步或者複製機制。如果在大數據量或者特殊的情況下寫入數據後可能會存在同步延遲的情況,可以調用master()方法進行主庫查詢操作。

在實際生產環境中,很多雲主機的數據庫分佈式實現機制和本地開發會有所區別,但通常會採下面用兩種方式:

第一種:提供了寫IP和讀IP(一般是虛擬IP),進行數據庫的讀寫分離操作; 第二種:始終保持同一個IP連接數據庫,內部會進行讀寫分離IP調度(阿里雲就是採用該方式)。

存儲過程調用

數據訪問層支持存儲過程調用,調用數據庫存儲過程使用下面的方法:

<code>$resultSet = Db::query('call procedure_name');
foreach ($resultSet as $result) {

}/<code>

存儲過程返回的是一個數據集,如果你的存儲過程不需要返回任何的數據,那麼也可以使用execute方法:

<code>Db::execute('call procedure_name');/<code>

存儲過程可以支持輸入和輸出參數,以及進行參數綁定操作。

<code>$resultSet = Db::query('call procedure_name(:in_param1,:in_param2,:out_param)', [
'in_param1' => $param1,
'in_param2' => [$param2, PDO::PARAM_INT],
'out_param' => [$outParam, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 4000],
]);/<code>

輸出參數的綁定必須額外使用PDO::PARAM_INPUT_OUTPUT,並且可以和輸入參數公用一個參數。

無論存儲過程內部做了什麼操作,每次存儲過程調用僅僅被當成一次查詢。

數據庫事務

5.0對數據庫事務的封裝更為完善,事務的支持由連接器類來完成,但查詢器類中也對事務進行了封裝調用,不過我們仍然只需要通過Db類便可完成事務操作。

使用事務處理的話,需要數據庫引擎支持事務處理。比如MySQL的MyISAM類型不支持事務處理,需要使用InnoDB引擎。

最簡單的方法是使用transaction方法操作數據庫事務,會自動控制事務處理,當發生任何異常會自動回滾,例如:

<code>Db::transaction(function () {
Db::table('user')->find(1);
Db::table('user')->where('id', 1)->save(['name' => 'thinkphp']);
Db::table('user')->delete(1);
});/<code>

也可以手動控制事務,例如:

<code>// 啟動事務
Db::startTrans();
try{
Db::table('user')->find(1);
Db::table('user')->where('id',1)->save(['name'=>'thinkphp']);
Db::table('user')->delete(1);
// 提交事務
Db::commit();
} catch (\\Exception $e) {
// 回滾事務
Db::rollback();
}/<code>

在事務操作的時候,確保你的數據庫連接是同一個,否則事務會失效,V5.0.9版本之前的db助手函數都是默認重新鏈接數據庫的,請不要在事務中使用。

總結

通過本章的學習,你應該瞭解了5.0的數據庫架構設計和數據庫抽象訪問層的組成,以及如何配置數據庫信息和使用基礎的原生查詢,掌握了用Db類的connect方法切換不同的數據庫連接,基本瞭解了存儲過程及事務的用法。後面一章,我們會先來了解下數據庫的創建和數據遷移,之後就會進入真正的數據庫查詢的學習了。


作者:寒冬夜行人_51a4
鏈接:https://www.jianshu.com/p/4926734b954c
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。


分享到:


相關文章: