本文介绍在 cakephp 中通过重写 `query::execute()` 方法,实现在双数据库(如 `d` 和 `c`)间安全复制 insert 操作的同时,自动对齐主键(pk)值,避免主键冲突或引用错位。核心在于先执行远端插入、再反向修正本地主键,并支持批量插入场景。
在使用 CakePHP 进行跨数据库复制(如将写操作从连接 d 同步至连接 c)时,一个常见但棘手的问题是:两个数据库的自增主键(如 id)彼此独立,无法保证值一致。若仅简单克隆并执行查询,会导致本地与远端记录虽内容相同,但主键不同——后续关联查询、外键引用或幂等更新将全部失效。
上述问题的解决方案并非“事后赋值”(如注释中尝试的 $this->_repository->{$primaryKey} = …),而是在 INSERT 执行前,主动预分配并注入匹配远端的主键值。关键逻辑分三步:
- 优先执行远端插入:先用 clone $this 创建副本,切换连接为 ‘c’ 并执行,确保远端已生成真实主键;
- 反向推导本地主键序列:通过 SELECT … ORDER BY pk DESC LIMIT 1 获取远端最新主键值(记为 $maxID),再根据本次插入行数 $count,计算出本地应使用的连续主键区间:[$maxID – $count + 1, $maxID];
- 动态重写 INSERT 子句:将原 VALUES 数组每行补入对应主键值,并更新 INSERT INTO … (cols) 的列定义,确保主键显式插入(绕过 AUTO_INCREMENT)。
以下是精简、健壮的实现代码(适配 CakePHP 4.x+):
public function execute()
{
if (!$this->isReplicate()) {
return parent::execute();
}
$table = $this->_repository->getTable();
$primaryKey = $this->_repository->getPrimaryKey();
// Step 1: Execute replica first on connection 'c'
$replica = clone $this;
$replica->setConnection(ConnectionManager::get('c'));
$replica->execute();
// Step 2: Handle INSERTs only — sync PKs
if (!empty($this->clause('insert'))) {
// Fetch latest PK from remote ('c') table
$maxID = $this->getConnection()
->execute("SELECT {$primaryKey} FROM {$table} ORDER BY {$primaryKey} DESC LIMIT 1")
->fetch('assoc')[$primaryKey];
$valuesClause = $this->clause('values');
$columns = $valuesClause->getColumns();
$values = $valuesClause->getValues();
$count = count($values);
// Extend columns to include PK
$columns[] = $primaryKey;
$this->insert($columns);
// Assign descending PKs: [maxID - count + 1, ..., maxID]
for ($i = 0; $i values($values);
}
return parent::execute();
}
⚠️ 重要注意事项: