一 安装及配置
composer require hyperf/db
php bin/hyperf.php vendor:publish hyperf/db
默认配置 config/autoload/db.php
如下,数据库支持多库配置,默认为 default
。
配置项 | 类型 | 默认值 | 备注 |
---|---|---|---|
driver | string | 无 | 数据库引擎 支持 pdo 和 mysql |
host | string | localhost | 数据库地址 |
port | int | 3306 | 数据库地址 |
database | string | 无 | 数据库默认 DB |
username | string | 无 | 数据库用户名 |
password | string | null | 数据库密码 |
charset | string | utf8 | 数据库编码 |
collation | string | utf8_unicode_ci | 数据库编码 |
fetch_mode | int | PDO::FETCH_ASSOC | PDO 查询结果集类型 |
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 配置 |
具体接口可以查看 Hyperf\DB\ConnectionInterface
。
方法名 | 返回值类型 | 备注 |
---|---|---|
beginTransaction | void | 开启事务 支持事务嵌套 |
commit | void | 提交事务 支持事务嵌套 |
rollBack | void | 回滚事务 支持事务嵌套 |
insert | int | 插入数据,返回主键 ID,非自增主键返回 0 |
execute | int | 执行 SQL,返回受影响的行数 |
query | array | 查询 SQL,返回结果集列表 |
fetch | array, object | 查询 SQL,返回结果集的首行数据 |
connection | self | 指定连接的数据库 |
二 使用
#使用 DB 实例
use Hyperf\Context\ApplicationContext;
use Hyperf\DB\DB;
$db = ApplicationContext::getContainer()->get(DB::class);
$res = $db->query('SELECT * FROM `user` WHERE gender = ?;', [1]);
#使用静态方法
use Hyperf\DB\DB;
$res = DB::query('SELECT * FROM `user` WHERE gender = ?;', [1]);
#使用匿名函数自定义方法
#此种方式可以允许用户直接操作底层的 PDO 或者 MySQL,所以需要自己处理兼容问题
use Hyperf\DB\DB;
$sql = 'SELECT * FROM `user` WHERE id = ?;';
$bindings = [2];
$mode = \PDO::FETCH_OBJ;
$res = DB::run(function (\PDO $pdo) use ($sql, $bindings, $mode) {
$statement = $pdo->prepare($sql);
$this->bindValues($statement, $bindings);
$statement->execute();
return $statement->fetchAll($mode);
});
三 测试
use Hyperf\DB\DB as AustereDb;
use Hyperf\Utils\ApplicationContext;
public function testdb1() {
$db = ApplicationContext::getContainer()->get(AustereDb::class);
$res = $db->query('SELECT * FROM `push_recode` WHERE id = ?;', [1]);
var_dump($res);
$res = AustereDb::query('SELECT * FROM `push_recode` WHERE id = ?;', [1]);
var_dump($res);
$sql = 'SELECT * FROM `push_recode` WHERE id = ?;';
$bindings = [1];
$mode = \PDO::FETCH_NUM;
$res = AustereDb::run(function (\PDO $pdo) use ($sql, $bindings, $mode) {
$statement = $pdo->prepare($sql);
$this->bindValues($statement, $bindings);
$statement->execute();
return $statement->fetchAll($mode);
});
var_dump($res);
}
测试结果
array(1) {
[0]=>
array(4) {
["id"]=>
int(1)
["is_push"]=>
int(1)
["push_time"]=>
string(19) "2024-04-11 08:10:13"
["created_at"]=>
NULL
}
}
array(1) {
[0]=>
array(4) {
["id"]=>
int(1)
["is_push"]=>
int(1)
["push_time"]=>
string(19) "2024-04-11 08:10:13"
["created_at"]=>
NULL
}
}
array(1) {
[0]=>
array(4) {
[0]=>
int(1)
[1]=>
int(1)
[2]=>
string(19) "2024-04-11 08:10:13"
[3]=>
NULL
}
}
可能由于版本问题,Hyperf\Context\ApplicationContext类不存在,所以使用Hyperf\Utils\ApplicationContext。
使用三种方法查询数据,使用DB实例、使用静态方法、使用PDO。
四 原理
获取容器参考hyperf console 执行-CSDN博客。
数据库配置获取参考hyperf console 执行-CSDN博客
用 php bin/hyperf.php vendor:publish hyperf/db 创建配置文件config\autoload\db.php后,其中使用.env文件。所以若项目数据库在.env中设置,配置文件可以不用改。
使用静态方法也是先获取容器,再调用对应方法。
使用run方法是根据配置文件获取数据库连接驱动,获取对应链接,默认是pdo。
若驱动为mysql,实际运行Hyperf\DB\MySQLConnection::run(Closure $closure)。
Closure类为一个闭包。
五 源码
#config\autoload\db.php
return [
'default' => [
'driver' => 'pdo',
'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', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'fetch_mode' => PDO::FETCH_ASSOC,
'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,
],
],
];
#Hyperf\DB\DB
protected $poolName;
public function __construct(PoolFactory $factory, string $poolName = 'default')
{
$this->factory = $factory;
$this->poolName = $poolName;
}
public static function __callStatic($name, $arguments)
{
$container = ApplicationContext::getContainer();
$db = $container->get(static::class);
return $db->{$name}(...$arguments);
}
public function __call($name, $arguments)
{
$hasContextConnection = Context::has($this->getContextKey());
$connection = $this->getConnection($hasContextConnection);
try {
$connection = $connection->getConnection();
$result = $connection->{$name}(...$arguments);
} catch (Throwable $exception) {
$result = $connection->retry($exception, $name, $arguments);
} finally {
if (! $hasContextConnection) {
if ($this->shouldUseSameConnection($name)) {
// Should storage the connection to coroutine context, then use defer() to release the connection.
Context::set($contextKey = $this->getContextKey(), $connection);
defer(function () use ($connection, $contextKey) {
Context::set($contextKey, null);
$connection->release();
});
} else {
// Release the connection after command executed.
$connection->release();
}
}
}
return $result;
}
private function getContextKey(): string
{
return sprintf('db.connection.%s', $this->poolName);
}
protected function getConnection(bool $hasContextConnection): AbstractConnection
{
$connection = null;
if ($hasContextConnection) {
$connection = Context::get($this->getContextKey());
}
if (! $connection instanceof AbstractConnection) {
$pool = $this->factory->getPool($this->poolName);
$connection = $pool->get();
}
return $connection;
}
#Hyperf\DB\Pool\PoolFactory
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getPool(string $name)
{
if (isset($this->pools[$name])) {
return $this->pools[$name];
}
$config = $this->container->get(ConfigInterface::class);
$driver = $config->get(sprintf('db.%s.driver', $name), 'pdo');
$class = $this->getPoolName($driver);
$pool = make($class, [$this->container, $name]);
if (! $pool instanceof Pool) {
throw new InvalidDriverException(sprintf('Driver %s is not invalid.', $driver));
}
return $this->pools[$name] = $pool;
}
protected function getPoolName(string $driver)
{
switch (strtolower($driver)) {
case 'mysql':
return MySQLPool::class;
case 'pdo':
return PDOPool::class;
}
if (class_exists($driver)) {
return $driver;
}
throw new DriverNotFoundException(sprintf('Driver %s is not found.', $driver));
}
#Hyperf\DB\Pool\MySQLPool
protected function createConnection(): ConnectionInterface
{
return new MySQLConnection($this->container, $this, $this->config);
}