hyperf/doc/zh-tw/db/quick-start.md
2019-12-12 16:24:04 +08:00

11 KiB
Raw Blame History

快速開始

前言

hyperf/database 衍生於 illuminate/database,我們對它進行了一些改造,大部分功能保持了相同。在這裡感謝一下 Laravel 開發組,實現瞭如此強大好用的 ORM 元件。

hyperf/database 元件是基於 illuminate/database 衍生出來的元件,我們對它進行了一些改造,從設計上是允許用於其它 PHP-FPM 框架或基於 Swoole 的框架中的,而在 Hyperf 裡就需要提一下 hyperf/db-connection 元件,它基於 hyperf/pool 實現了資料庫連線池並對模型進行了新的抽象以它作為橋樑Hyperf 才能把資料庫元件及事件元件接入進來。

安裝

Hyperf 框架

composer require hyperf/db-connection

其它框架

composer require hyperf/database

配置

預設配置如下,資料庫支援多庫配置,預設為 default

配置項 型別 預設值 備註
driver string 資料庫引擎
host string 資料庫地址
database string 資料庫預設 DB
username string 資料庫使用者名稱
password string null 資料庫密碼
charset string utf8 資料庫編碼
collation string utf8_unicode_ci 資料庫編碼
prefix string '' 資料庫模型字首
pool.min_connections int 1 連線池內最少連線數
pool.max_connections int 10 連線池內最大連線數
pool.connect_timeout float 10.0 連線等待超時時間
pool.wait_timeout float 3.0 超時時間
pool.heartbeat int -1 心跳
pool.max_idle_time float 60.0 最大閒置時間
options array PDO 配置
<?php

return [
    'default' => [
        'driver' => env('DB_DRIVER', 'mysql'),
        'host' => env('DB_HOST', 'localhost'),
        'port' => env('DB_PORT', 3306),
        'database' => env('DB_DATABASE', 'hyperf'),
        'username' => env('DB_USERNAME', 'root'),
        'password' => env('DB_PASSWORD', ''),
        'charset' => env('DB_CHARSET', 'utf8'),
        'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
        'prefix' => env('DB_PREFIX', ''),
        'pool' => [
            'min_connections' => 1,
            'max_connections' => 10,
            'connect_timeout' => 10.0,
            'wait_timeout' => 3.0,
            'heartbeat' => -1,
            'max_idle_time' => (float)env('DB_MAX_IDLE_TIME', 60),
        ]
    ],
];

有時候使用者需要修改 PDO 預設配置,比如所有欄位需要返回為 string。這時候就需要修改 PDO 配置項 ATTR_STRINGIFY_FETCHES 為 true。

<?php

return [
    'default' => [
        'driver' => env('DB_DRIVER', 'mysql'),
        'host' => env('DB_HOST', 'localhost'),
        'port' => env('DB_PORT', 3306),
        'database' => env('DB_DATABASE', 'hyperf'),
        'username' => env('DB_USERNAME', 'root'),
        'password' => env('DB_PASSWORD', ''),
        'charset' => env('DB_CHARSET', 'utf8'),
        'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
        'prefix' => env('DB_PREFIX', ''),
        'pool' => [
            'min_connections' => 1,
            'max_connections' => 10,
            'connect_timeout' => 10.0,
            'wait_timeout' => 3.0,
            'heartbeat' => -1,
            'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
        ],
        'options' => [
            // 框架預設配置
            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,
        ],
    ],
];

讀寫分離

有時候你希望 SELECT 語句使用一個數據庫連線,而 INSERTUPDATE,和 DELETE 語句使用另一個數據庫連線。在 Hyperf 中,無論你是使用原生查詢,查詢構造器,或者是模型,都能輕鬆的實現

為了弄明白讀寫分離是如何配置的,我們先來看個例子:

<?php

return [
    'default' => [
        'driver' => env('DB_DRIVER', 'mysql'),
        'read' => [
            'host' => ['192.168.1.1'],
        ],
        'write' => [
            'host' => ['196.168.1.2'],
        ],
        'sticky'    => true,
        'database' => env('DB_DATABASE', 'hyperf'),
        'username' => env('DB_USERNAME', 'root'),
        'password' => env('DB_PASSWORD', ''),
        'charset' => env('DB_CHARSET', 'utf8'),
        'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
        'prefix' => env('DB_PREFIX', ''),
        'pool' => [
            'min_connections' => 1,
            'max_connections' => 10,
            'connect_timeout' => 10.0,
            'wait_timeout' => 3.0,
            'heartbeat' => -1,
            'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
        ],
    ],
];

注意在以上的例子中,配置陣列中增加了三個鍵,分別是 read writestickyreadwrite 的鍵都包含一個鍵為 host 的陣列。而 readwrite 的其他資料庫都在鍵為 mysql 的陣列中。

如果你想重寫主陣列中的配置,只需要修改 readwrite 陣列即可。所以,這個例子中: 192.168.1.1 將作為 「讀」 連線主機,而 192.168.1.2 將作為 「寫」 連線主機。這兩個連線會共享 mysql 陣列的各項配置,如資料庫的憑據(使用者名稱 / 密碼),字首,字元編碼等。

sticky 是一個 可選值,它可用於立即讀取在當前請求週期內已寫入資料庫的記錄。若 sticky 選項被啟用,並且當前請求週期內執行過 「寫」 操作,那麼任何 「讀」 操作都將使用 「寫」 連線。這樣可確保同一個請求週期內寫入的資料可以被立即讀取到,從而避免主從延遲導致資料不一致的問題。不過是否啟用它,取決於應用程式的需求。

多庫配置

多庫配置如下。

<?php

return [
    'default' => [
        'driver' => env('DB_DRIVER', 'mysql'),
        'host' => env('DB_HOST', 'localhost'),
        'database' => env('DB_DATABASE', 'hyperf'),
        'username' => env('DB_USERNAME', 'root'),
        'password' => env('DB_PASSWORD', ''),
        'charset' => env('DB_CHARSET', 'utf8'),
        'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
        'prefix' => env('DB_PREFIX', ''),
        'pool' => [
            'min_connections' => 1,
            'max_connections' => 10,
            'connect_timeout' => 10.0,
            'wait_timeout' => 3.0,
            'heartbeat' => -1,
            'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
        ],
    ],
    'test'=>[
        'driver' => env('DB_DRIVER', 'mysql'),
        'host' => env('DB_HOST2', 'localhost'),
        'database' => env('DB_DATABASE', 'hyperf'),
        'username' => env('DB_USERNAME', 'root'),
        'password' => env('DB_PASSWORD', ''),
        'charset' => env('DB_CHARSET', 'utf8'),
        'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
        'prefix' => env('DB_PREFIX', ''),
        'pool' => [
            'min_connections' => 1,
            'max_connections' => 10,
            'connect_timeout' => 10.0,
            'wait_timeout' => 3.0,
            'heartbeat' => -1,
            'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
        ],
    ],
];

使用時,只需要規定 connectiontest,就可以使用 test 中的配置,如下。

<?php

use Hyperf\DbConnection\Db;
// default
Db::select('SELECT * FROM user;');
Db::connection('default')->select('SELECT * FROM user;');

// test
Db::connection('test')->select('SELECT * FROM user;');

模型中修改 connection 欄位,即可使用對應配置,例如一下 Model 使用 test 配置。

<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://doc.hyperf.io
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Model;

/**
 * @property int $id
 * @property string $mobile
 * @property string $realname
 */
class User extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'user';

    /**
     * The connection name for the model.
     *
     * @var string
     */
    protected $connection = 'test';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['id', 'mobile', 'realname'];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = ['id' => 'integer'];
}

執行原生 SQL 語句

配置好資料庫後,便可以使用 Hyperf\DbConnection\Db 進行查詢。

Query 查詢類

這裡主要包括 Select、屬性為 READS SQL DATA 的儲存過程、函式等查詢語句。

select 方法將始終返回一個數組,陣列中的每個結果都是一個 StdClass 物件

<?php

use Hyperf\DbConnection\Db;

$users = Db::select('SELECT * FROM `user` WHERE gender = ?',[1]);  //  返回array 

foreach($users as $user){
    echo $user->name;
}

Execute 執行類

這裡主要包括 InsertUpdateDelete,屬性為 MODIFIES SQL DATA 的儲存過程等執行語句。

<?php

use Hyperf\DbConnection\Db;

$inserted = Db::insert('INSERT INTO user (id, name) VALUES (?, ?)', [1, 'Hyperf']); // 返回是否成功 bool

$affected = Db::update('UPDATE user set name = ? WHERE id = ?', ['John', 1]); // 返回受影響的行數 int

$affected = Db::delete('DELETE FROM user WHERE id = ?', [1]); // 返回受影響的行數 int

$result = Db::statement("CALL pro_test(?, '?')", [1, 'your words']);  // 返回 bool  CALL pro_test(??) 為儲存過程,屬性為 MODIFIES SQL DATA

自動管理資料庫事務

你可以使用 Dbtransaction 方法在資料庫事務中執行一組操作。如果事務的閉包 Closure 中出現一個異常,事務將會回滾。如果事務閉包 Closure 執行成功,事務將自動提交。一旦你使用了 transaction 就不再需要擔心手動回滾或提交的問題:

<?php
use Hyperf\DbConnection\Db;

Db::transaction(function () {
    Db::table('user')->update(['votes' => 1]);

    Db::table('posts')->delete();
});

手動管理資料庫事務

如果你想要手動開始一個事務,並且對回滾和提交能夠完全控制,那麼你可以使用 DbbeginTransaction, commit, rollBack:

use Hyperf\DbConnection\Db;

Db::beginTransaction();
try{

    // Do something...

    Db::commit();
} catch(\Throwable $ex){
    Db::rollBack();
}