我们已经将CakeBehavior和CakePHP 3.6一起使用了一段时间。最近,在一棵大树(大约90000个节点)下,我们遇到了问题。
删除节点时,在cakephp / src / ORM / Behavior / TreeBehavior中调用beforeDelete()函数:
/**
* Also deletes the nodes in the subtree of the entity to be delete
*
* @param \Cake\Event\Event $event The beforeDelete event that was fired
* @param \Cake\Datasource\EntityInterface $entity The entity that is going to be saved
* @return void
*/
public function beforeDelete(Event $event,EntityInterface $entity)
{
$config = $this->getconfig();
$this->_ensureFields($entity);
$left = $entity->get($config['left']);
$right = $entity->get($config['right']);
$diff = $right - $left + 1;
if ($diff > 2) {
$query = $this->_scope($this->_table->query())
->delete()
->where(function ($exp) use ($config,$left,$right) {
/* @var \Cake\Database\Expression\QueryExpression $exp */
return $exp
->gte($config['leftField'],$left + 1)
->lte($config['leftField'],$right - 1);
});
$statement = $query->execute();
$statement->closeCursor();
}
$this->_sync($diff,'-',"> {$right}");
}
此函数调用_sync()函数以更新树的左右值:
/**
* Auxiliary function used to automatically alter the value of both the left and
* right columns by a certain amount that match the passed conditions
*
* @param int $shift the value to use for operating the left and right columns
* @param string $dir The operator to use for shifting the value (+/-)
* @param string $conditions a SQL snipped to be used for comparing left or right
* against it.
* @param bool $mark whether to mark the updated values so that they can not be
* modified by future calls to this function.
* @return void
*/
protected function _sync($shift,$dir,$conditions,$mark = false)
{
$config = $this->_config;
foreach ([$config['leftField'],$config['rightField']] as $field) {
$query = $this->_scope($this->_table->query());
$exp = $query->newExpr();
$movement = clone $exp;
$movement->add($field)->add((string)$shift)->setConjunction($dir);
$inverse = clone $exp;
$movement = $mark ?
$inverse->add($movement)->setConjunction('*')->add('-1') :
$movement;
$where = clone $exp;
$where->add($field)->add($conditions)->setConjunction('');
$query->update()
->set($exp->eq($field,$movement))
->where($where);
$query->execute()->closeCursor();
}
}
但是,似乎此_sync()函数执行的更新查询未包装在事务中,并且看起来像这样:
UPDATE `leafs` SET `lft` = ((`lft` - 12)) WHERE ((`lft` > 52044))
UPDATE `leafs` SET `rght` = ((`rght` - 12)) WHERE ((`rght` > 52044))
这些不应该像这样包装在交易中吗?
START TRANSactION
UPDATE `leafs` SET `lft` = ((`lft` - 12)) WHERE ((`lft` > 52044))
UPDATE `leafs` SET `rght` = ((`rght` - 12)) WHERE ((`rght` > 52044))
COMMIT
...尤其是因为更新的值(lft和rght)也出现在WHERE子句中。我们在树损坏方面遇到了一些问题,并想知道这是否可能是一个原因……尤其是当在一棵大树上快速连续执行多个操作时。