-
Notifications
You must be signed in to change notification settings - Fork 2
事务和并发
free2one edited this page Jul 13, 2023
·
1 revision
$user = new User();
$user
->setUserName('小明')
->setGender(Gender::Male);
$em = EntityManagerFactory::getManager();
$em->persist($user);
$em->flush();
调用EntityManager#flush()
时将自动提交/回滚事务。
$em = EntityManagerFactory::getManager();
$em->beginTransaction();
try {
$user = new User();
$user
->setUserName('小明')
->setGender(Gender::Male);
$em->persist($user);
$em->flush();
$em->commit();
} catch (\Throwable $e) {
$em->rollBack();
throw $e;
}
或以下更便捷的方式
$em = EntityManagerFactory::getManager();
$em->wrapInTransaction(function () use ($em) {
$user = new User();
$user
->setUserName('小明')
->setGender(Gender::Male);
$em->persist($user);
});
当使用隐式事务并且在EntityManager#flush()
期间发生异常时,事务将自动回滚并关闭EntityManager。
当使用显式事务并且发生异常时,你应该立即回滚事务,并通过调用EntityManager#close()
关闭EntityManager,随后丢弃EntityManager。
如果您打算在异常发生后启动另一个工作单元,您应该使用一个新的EntityManager。
EntityManagerFactory::getManager()
会保证在协程内永远返回的是相同的EntityManager未关闭实例。当检测到EntityManager已被关闭时,EntityManagerFactory会重新初始化EntityManager,并设置到当前协程上下文中。
Doctrine集成了对自动乐观锁定的支持。
我们仍然通过User
作为例子进行演示。首先我们修改user表结构,增加version列,并设置其类型为int unsigned
。随后,我们同步修改User
类,增加相应的属性及注解。
class User
{
#[ORM\Version, ORM\Column(type: Types::INTEGER)]
private int $version;
}
下面,我们模拟并发场景下对同一数据行进行修改。
$batchNum = 10;
$channel = new Channel($batchNum);
for ($i = 1; $i <= $batchNum; ++$i) {
di()->get(Coroutine::class)->create(function () use (&$channel, $i) {
try {
$em = EntityManagerFactory::getManager();
$user = $em->find(User::class, 1);
sleep(1);
$user->setUserName('小明_' . $i . microtime());
$em->flush();
var_dump('sucess:' . $i);
} catch (\Doctrine\ORM\OptimisticLockException $e) {
var_dump($e->getMessage() . ':' . $i);
}
$channel->push($i);
});
}
for ($len = $channel->getCapacity() + 1; --$len;) {
$channel->pop(-1);
}
执行以上代码后,最终只输出一行sucess语句。
待补充