快速开始

前言

hyperf/database 衍生于 illuminate/database,我们对它进行了一些改造,大部分功能保持了相同。在这里感谢一下 Laravel 开发组,实现了如此强大好用的 ORM 组件。

hyperf/database 组件是基于 illuminate/database 衍生出来的组件,我们对它进行了一些改造,从设计上是允许用于其它 PHP-FPM 框架或基于 Swoole 的框架中的,而在 Hyperf 里就需要提一下 hyperf/db-connection 组件,它基于 hyperf/pool 实现了数据库连接池并对模型进行了新的抽象,以它作为桥梁,Hyperf 才能把数据库组件及事件组件接入进来。

安装

Hyperf 框架

  1. composer require hyperf/db-connection

其它框架

  1. 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 ‘’ 数据库模型前缀
timezone string null 数据库时区
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 配置
  1. <?php
  2. return [
  3. 'default' => [
  4. 'driver' => env('DB_DRIVER', 'mysql'),
  5. 'host' => env('DB_HOST', 'localhost'),
  6. 'port' => env('DB_PORT', 3306),
  7. 'database' => env('DB_DATABASE', 'hyperf'),
  8. 'username' => env('DB_USERNAME', 'root'),
  9. 'password' => env('DB_PASSWORD', ''),
  10. 'charset' => env('DB_CHARSET', 'utf8'),
  11. 'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
  12. 'prefix' => env('DB_PREFIX', ''),
  13. 'pool' => [
  14. 'min_connections' => 1,
  15. 'max_connections' => 10,
  16. 'connect_timeout' => 10.0,
  17. 'wait_timeout' => 3.0,
  18. 'heartbeat' => -1,
  19. 'max_idle_time' => (float)env('DB_MAX_IDLE_TIME', 60),
  20. ]
  21. ],
  22. ];

有时候用户需要修改 PDO 默认配置,比如所有字段需要返回为 string。这时候就需要修改 PDO 配置项 ATTR_STRINGIFY_FETCHES 为 true。

  1. <?php
  2. return [
  3. 'default' => [
  4. 'driver' => env('DB_DRIVER', 'mysql'),
  5. 'host' => env('DB_HOST', 'localhost'),
  6. 'port' => env('DB_PORT', 3306),
  7. 'database' => env('DB_DATABASE', 'hyperf'),
  8. 'username' => env('DB_USERNAME', 'root'),
  9. 'password' => env('DB_PASSWORD', ''),
  10. 'charset' => env('DB_CHARSET', 'utf8'),
  11. 'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
  12. 'prefix' => env('DB_PREFIX', ''),
  13. 'pool' => [
  14. 'min_connections' => 1,
  15. 'max_connections' => 10,
  16. 'connect_timeout' => 10.0,
  17. 'wait_timeout' => 3.0,
  18. 'heartbeat' => -1,
  19. 'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
  20. ],
  21. 'options' => [
  22. // 框架默认配置
  23. PDO::ATTR_CASE => PDO::CASE_NATURAL,
  24. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  25. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  26. PDO::ATTR_STRINGIFY_FETCHES => false,
  27. // 如果使用的为非原生 MySQL 或云厂商提供的 DB 如从库/分析型实例等不支持 MySQL prepare 协议的, 将此项设置为 true
  28. PDO::ATTR_EMULATE_PREPARES => false,
  29. ],
  30. ],
  31. ];

读写分离

有时候你希望 SELECT 语句使用一个数据库连接,而 INSERTUPDATE,和 DELETE 语句使用另一个数据库连接。在 Hyperf 中,无论你是使用原生查询,查询构造器,或者是模型,都能轻松的实现

为了弄明白读写分离是如何配置的,我们先来看个例子:

  1. <?php
  2. return [
  3. 'default' => [
  4. 'driver' => env('DB_DRIVER', 'mysql'),
  5. 'read' => [
  6. 'host' => ['192.168.1.1'],
  7. ],
  8. 'write' => [
  9. 'host' => ['196.168.1.2'],
  10. ],
  11. 'sticky' => true,
  12. 'database' => env('DB_DATABASE', 'hyperf'),
  13. 'username' => env('DB_USERNAME', 'root'),
  14. 'password' => env('DB_PASSWORD', ''),
  15. 'charset' => env('DB_CHARSET', 'utf8'),
  16. 'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
  17. 'prefix' => env('DB_PREFIX', ''),
  18. 'pool' => [
  19. 'min_connections' => 1,
  20. 'max_connections' => 10,
  21. 'connect_timeout' => 10.0,
  22. 'wait_timeout' => 3.0,
  23. 'heartbeat' => -1,
  24. 'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
  25. ],
  26. ],
  27. ];

注意在以上的例子中,配置数组中增加了三个键,分别是 readwritestickyreadwrite 的键都包含一个键为 host 的数组。而 readwrite 的其他数据库都在键为 mysql 的数组中。

如果你想重写主数组中的配置,只需要修改 readwrite 数组即可。所以,这个例子中: 192.168.1.1 将作为 「读」 连接主机,而 192.168.1.2 将作为 「写」 连接主机。这两个连接会共享 mysql 数组的各项配置,如数据库的凭据(用户名 / 密码),前缀,字符编码等。

sticky 是一个 可选值,它可用于立即读取在当前请求周期内已写入数据库的记录。若 sticky 选项被启用,并且当前请求周期内执行过 「写」 操作,那么任何 「读」 操作都将使用 「写」 连接。这样可确保同一个请求周期内写入的数据可以被立即读取到,从而避免主从延迟导致数据不一致的问题。不过是否启用它,取决于应用程序的需求。

多库配置

多库配置如下。

  1. <?php
  2. return [
  3. 'default' => [
  4. 'driver' => env('DB_DRIVER', 'mysql'),
  5. 'host' => env('DB_HOST', 'localhost'),
  6. 'database' => env('DB_DATABASE', 'hyperf'),
  7. 'username' => env('DB_USERNAME', 'root'),
  8. 'password' => env('DB_PASSWORD', ''),
  9. 'charset' => env('DB_CHARSET', 'utf8'),
  10. 'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
  11. 'prefix' => env('DB_PREFIX', ''),
  12. 'pool' => [
  13. 'min_connections' => 1,
  14. 'max_connections' => 10,
  15. 'connect_timeout' => 10.0,
  16. 'wait_timeout' => 3.0,
  17. 'heartbeat' => -1,
  18. 'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
  19. ],
  20. ],
  21. 'test'=>[
  22. 'driver' => env('DB_DRIVER', 'mysql'),
  23. 'host' => env('DB_HOST2', 'localhost'),
  24. 'database' => env('DB_DATABASE', 'hyperf'),
  25. 'username' => env('DB_USERNAME', 'root'),
  26. 'password' => env('DB_PASSWORD', ''),
  27. 'charset' => env('DB_CHARSET', 'utf8'),
  28. 'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
  29. 'prefix' => env('DB_PREFIX', ''),
  30. 'pool' => [
  31. 'min_connections' => 1,
  32. 'max_connections' => 10,
  33. 'connect_timeout' => 10.0,
  34. 'wait_timeout' => 3.0,
  35. 'heartbeat' => -1,
  36. 'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
  37. ],
  38. ],
  39. ];

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

  1. <?php
  2. use Hyperf\DbConnection\Db;
  3. // default
  4. Db::select('SELECT * FROM user;');
  5. Db::connection('default')->select('SELECT * FROM user;');
  6. // test
  7. Db::connection('test')->select('SELECT * FROM user;');

模型中修改 connection 字段,即可使用对应配置,例如一下 Model 使用 test 配置。

  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://doc.hyperf.io
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace App\Model;
  12. /**
  13. * @property int $id
  14. * @property string $mobile
  15. * @property string $realname
  16. */
  17. class User extends Model
  18. {
  19. /**
  20. * The table associated with the model.
  21. *
  22. * @var string
  23. */
  24. protected $table = 'user';
  25. /**
  26. * The connection name for the model.
  27. *
  28. * @var string
  29. */
  30. protected $connection = 'test';
  31. /**
  32. * The attributes that are mass assignable.
  33. *
  34. * @var array
  35. */
  36. protected $fillable = ['id', 'mobile', 'realname'];
  37. /**
  38. * The attributes that should be cast to native types.
  39. *
  40. * @var array
  41. */
  42. protected $casts = ['id' => 'integer'];
  43. }

执行原生 SQL 语句

配置好数据库后,便可以使用 Hyperf\DbConnection\Db 进行查询。

Query 查询类

这里主要包括 Select、属性为 READS SQL DATA 的存储过程、函数等查询语句。

select 方法将始终返回一个数组,数组中的每个结果都是一个 StdClass 对象

  1. <?php
  2. use Hyperf\DbConnection\Db;
  3. $users = Db::select('SELECT * FROM `user` WHERE gender = ?',[1]); // 返回array
  4. foreach($users as $user){
  5. echo $user->name;
  6. }

Execute 执行类

这里主要包括 InsertUpdateDelete,属性为 MODIFIES SQL DATA 的存储过程等执行语句。

  1. <?php
  2. use Hyperf\DbConnection\Db;
  3. $inserted = Db::insert('INSERT INTO user (id, name) VALUES (?, ?)', [1, 'Hyperf']); // 返回是否成功 bool
  4. $affected = Db::update('UPDATE user set name = ? WHERE id = ?', ['John', 1]); // 返回受影响的行数 int
  5. $affected = Db::delete('DELETE FROM user WHERE id = ?', [1]); // 返回受影响的行数 int
  6. $result = Db::statement("CALL pro_test(?, '?')", [1, 'your words']); // 返回 bool CALL pro_test(?,?) 为存储过程,属性为 MODIFIES SQL DATA

自动管理数据库事务

你可以使用 Dbtransaction 方法在数据库事务中运行一组操作。如果事务的闭包 Closure 中出现一个异常,事务将会回滚。如果事务闭包 Closure 执行成功,事务将自动提交。一旦你使用了 transaction , 就不再需要担心手动回滚或提交的问题:

  1. <?php
  2. use Hyperf\DbConnection\Db;
  3. Db::transaction(function () {
  4. Db::table('user')->update(['votes' => 1]);
  5. Db::table('posts')->delete();
  6. });

手动管理数据库事务

如果你想要手动开始一个事务,并且对回滚和提交能够完全控制,那么你可以使用 DbbeginTransaction, commit, rollBack:

  1. use Hyperf\DbConnection\Db;
  2. Db::beginTransaction();
  3. try{
  4. // Do something...
  5. Db::commit();
  6. } catch(\Throwable $ex){
  7. Db::rollBack();
  8. }

输出刚刚执行的 SQL

当前方法仅能用于开发环境,线上部署前一定要去掉,不然会引起严重的内存泄露和数据混淆。

线上记录 SQL,请使用 事件监听

  1. <?php
  2. use Hyperf\DbConnection\Db;
  3. use Hyperf\Utils\Arr;
  4. use App\Model\Book;
  5. // 启用 SQL 数据记录功能
  6. Db::enableQueryLog();
  7. $book = Book::query()->find(1);
  8. // 打印最后一条 SQL 相关数据
  9. var_dump(Arr::last(Db::getQueryLog()));