教程地址:Hyperf
composer require hyperf/db-connection
composer require hyperf/database
配置项 | 类型 | 默认值 | 备注 |
---|---|---|---|
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 配置 |
options相关pdo文档:PHP: PDO - Manual
由于hyperf/database 衍生于 illuminate/database 。
illuminate/database多库配置和读写分离参考illuminate/database 使用 四-CSDN博客 ;原生查询和sql输出参考illuminate/database 使用 五-CSDN博客 。
根据文档描述:如果事务的闭包 Closure
中出现一个异常,事务将会回滚。如果事务闭包 Closure
执行成功,事务将自动提交。
确实比手动方便,但是还得查下报错怎么处理。
根据之前文章,应该明白Illuminate\Database中调用顺序,即从Illuminate\Database\Capsule\Manager最后会调用到Illuminate\Database\Connectionl。Connectionl中调用Concerns\ManagesTransactions类。transaction()方法在ManagesTransactions类中被调用。
源码如下
trait ManagesTransactions
{
/**
* Execute a Closure within a transaction.
*
* @param \Closure $callback
* @param int $attempts
* @return mixed
*
* @throws \Throwable
*/
public function transaction(Closure $callback, $attempts = 1)
{
for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) {
$this->beginTransaction();
// We'll simply execute the given callback within a try / catch block and if we
// catch any exception we can rollback this transaction so that none of this
// gets actually persisted to a database or stored in a permanent fashion.
try {
$callbackResult = $callback($this);
}
// If we catch an exception we'll rollback this transaction and try again if we
// are not out of attempts. If we are out of attempts we will just throw the
// exception back out and let the developer handle an uncaught exceptions.
catch (Throwable $e) {
$this->handleTransactionException(
$e, $currentAttempt, $attempts
);
continue;
}
try {
if ($this->transactions == 1) {
$this->getPdo()->commit();
}
$this->transactions = max(0, $this->transactions - 1);
if ($this->transactions == 0) {
optional($this->transactionsManager)->commit($this->getName());
}
} catch (Throwable $e) {
$this->handleCommitTransactionException(
$e, $currentAttempt, $attempts
);
continue;
}
$this->fireConnectionEvent('committed');
return $callbackResult;
}
}
/**
* Handle an exception encountered when running a transacted statement.
*
* @param \Throwable $e
* @param int $currentAttempt
* @param int $maxAttempts
* @return void
*
* @throws \Throwable
*/
protected function handleTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
{
// On a deadlock, MySQL rolls back the entire transaction so we can't just
// retry the query. We have to throw this exception all the way out and
// let the developer handle it in another way. We will decrement too.
if ($this->causedByConcurrencyError($e) &&
$this->transactions > 1) {
$this->transactions--;
optional($this->transactionsManager)->rollback(
$this->getName(), $this->transactions
);
throw $e;
}
// If there was an exception we will rollback this transaction and then we
// can check if we have exceeded the maximum attempt count for this and
// if we haven't we will return and try this query again in our loop.
$this->rollBack();
if ($this->causedByConcurrencyError($e) &&
$currentAttempt < $maxAttempts) {
return;
}
throw $e;
}
/**
* Handle an exception encountered when committing a transaction.
*
* @param \Throwable $e
* @param int $currentAttempt
* @param int $maxAttempts
* @return void
*
* @throws \Throwable
*/
protected function handleCommitTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
{
$this->transactions = max(0, $this->transactions - 1);
if ($this->causedByConcurrencyError($e) &&
$currentAttempt < $maxAttempts) {
return;
}
if ($this->causedByLostConnection($e)) {
$this->transactions = 0;
}
throw $e;
}
……
}
如源码所示,闭包执行错误,执行handleTransactionException()方法,处理回滚,并抛出异常。
$this->transactions--大概是处理事务嵌套相关代码。
提交异常,执行handleCommitTransactionException()方法,嵌套事务大概会继续执行,最后抛出异常。
测试代码
function test4()
{
Capsule::transaction(function () {
Capsule::table('userinfo')->where(['id' => 1])->update(['votes' => 1]);
});
}
test4();
运行结果
Fatal error: Uncaught PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'votes' in 'field list' in D:\workspace\php\wj_test\illuminate_database\vendor\illuminate\database\Connection.php on line 712
Illuminate\Database\QueryException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'votes' in 'field list' (SQL: update `userinfo` set `votes` = 1 where (`id` = 1)) in D:\workspace\php\wj_test\illuminate_database\vendor\illuminate\database\Connection.php on line 712
Call Stack:
0.0009 411368 1. {main}() D:\workspace\php\wj_test\illuminate_database\test.php:0
0.0208 1925160 2. test4() D:\workspace\php\wj_test\illuminate_database\test.php:119
0.0208 1925480 3. Illuminate\Database\Capsule\Manager::transaction(class Closure) D:\workspace\php\wj_test\illuminate_database\test.php:117
0.0208 1925856 4. Illuminate\Database\Capsule\Manager::__callStatic(string(11), array(1)) D:\workspace\php\wj_test\illuminate_database\test.php:117
0.0269 2634512 5. Illuminate\Database\MySqlConnection->transaction(class Closure, ???) D:\workspace\php\wj_test\illuminate_database\vendor\illuminate\database\Capsule\Manager.php:200
0.0705 3895296 6. Illuminate\Database\MySqlConnection->handleTransactionException(class Illuminate\Database\QueryException, long, long) D:\workspace\php\wj_test\illuminate_database\vendor\illuminate\database\Concerns\ManagesTransactions.php:37
此时update执行Illuminate\Database\Connection::update(),update调用Connection::run()执行,run()通过Connection::runQueryCallback()执行,产生PDO异常,再将PDO异常传递给\Illuminate\Database\QueryException异常。
使用throw 会直接输出异常。
PDOException:PHP: PDOException - Manual
function test5()
{
Capsule::beginTransaction();
try {
Capsule::table('userinfo')->where(['id' => 1])->update(['votes' => 1]);
Capsule::commit();
} catch (\Throwable $th) {
var_dump($th->getMessage());
Capsule::rollBack();
}
}
test5();
和之前是同样的道理,代码最后执行都是Connectionl类,Connectionl::beginTransaction()、Connectionl::commit()、Connectionl::rollback(),都是Connectionl中调用的ManagesTransactions类中的方法。
例子中Capsule类相当于官网的DB类。
参考
illuminate/database 使用 一-CSDN博客
Hyperf
自己写的博客有涉及一些运行原理,官网的例子比较多。