为同一模型使用多个数据库
Using multiple databases for the same model
我正在开发一些需要连接到两个独立数据库的 API,包含类似的表但数据不同。
我已经在 app.php
和 app_local.php
中配置了两个数据源,它们都可以访问。
经过一些研究,我发现 ConnectionManager::alias
应该是我所需要的,所以我想出了这个:
// Fetching `energy` DB data
ConnectionManager::alias('energy', 'default');
$energy = $this->Users->find()->first();
debug($energy);
ConnectionManager::dropAlias('default');
// Fetching `default` (gas) DB data
$gas = $this->Users->find()->first();
debug($gas);
问题是,如果我 运行 那些像这样的查询,调试会给出相同的数据,这意味着来自 energy
数据库的数据被打印了两次。
但是:
- 如果我只 运行
energy
找到它给我能源数据库的正确结果。
- 如果我只 运行
gas
找到它给了我来自气体数据库的正确结果。
看起来实际只使用了第一个数据源。
我做错了什么?
一旦 table class 被实例化,它将保存实例化时分配给它的连接实例(\Cake\ORM\Table::__construct()
的 connection
选项) ,分别是 table 第一次尝试获取连接 (\Cake\ORM\Table::getConnection()
) 时创建的那个。
所以单独删除别名不会做任何事情,对 table 的下一次调用将使用 table 已经拥有的连接实例,它不会再次尝试获取连接靠自己。
由于您希望连接更改影响所有内容,因此您必须在删除连接别名后清除 table 注册表,然后再次加载 table,这样它们就会使用实际的 default
连接再次创建(如果您只想影响特定实例,您可以通过 setConnection()
分配一个新的连接实例)。为避免 运行 在 创建 别名时与已创建的 table 实例出现相同的问题,您很可能还应该在 之后清除注册表.
基本示例:
ConnectionManager::alias('energy', 'default');
$this->getTableLocator()->clear();
// table instances created after this point will respect the alias
$this->Users = null;
$this->loadModel('Users');
$energy = $this->Users->find()->first();
ConnectionManager::dropAlias('default');
$this->getTableLocator()->clear();
// table instances created after this point will use the non-aliased connection
$this->Users = null;
$this->loadModel('Users');
$gas = $this->Users->find()->first();
如您所见,这不仅需要再次加载 model/table,还需要取消设置 属性 上的实例,否则 loadModel()
将不会实际加载 table,而是 return 已经在 $this->Users
属性.
上设置的实例
我建议你切换到通过 table 定位器获取你的模型,即使在你的控制器中,这样你就可以跳过这个尴尬的未设置的东西,并且你可以减少意外访问存储的旧实例的风险在 $this->Users
:
这样的属性上
ConnectionManager::alias('energy', 'default');
$this->getTableLocator()->clear();
$energy = $this->getTableLocator()-get('Users')->find()->first();
ConnectionManager::dropAlias('default');
$this->getTableLocator()->clear();
$gas = $this->getTableLocator()-get('Users')->find()->first();
您可能还应该考虑将此连接切换集中在某个服务中。
我正在开发一些需要连接到两个独立数据库的 API,包含类似的表但数据不同。
我已经在 app.php
和 app_local.php
中配置了两个数据源,它们都可以访问。
经过一些研究,我发现 ConnectionManager::alias
应该是我所需要的,所以我想出了这个:
// Fetching `energy` DB data
ConnectionManager::alias('energy', 'default');
$energy = $this->Users->find()->first();
debug($energy);
ConnectionManager::dropAlias('default');
// Fetching `default` (gas) DB data
$gas = $this->Users->find()->first();
debug($gas);
问题是,如果我 运行 那些像这样的查询,调试会给出相同的数据,这意味着来自 energy
数据库的数据被打印了两次。
但是:
- 如果我只 运行
energy
找到它给我能源数据库的正确结果。 - 如果我只 运行
gas
找到它给了我来自气体数据库的正确结果。
看起来实际只使用了第一个数据源。
我做错了什么?
一旦 table class 被实例化,它将保存实例化时分配给它的连接实例(\Cake\ORM\Table::__construct()
的 connection
选项) ,分别是 table 第一次尝试获取连接 (\Cake\ORM\Table::getConnection()
) 时创建的那个。
所以单独删除别名不会做任何事情,对 table 的下一次调用将使用 table 已经拥有的连接实例,它不会再次尝试获取连接靠自己。
由于您希望连接更改影响所有内容,因此您必须在删除连接别名后清除 table 注册表,然后再次加载 table,这样它们就会使用实际的 default
连接再次创建(如果您只想影响特定实例,您可以通过 setConnection()
分配一个新的连接实例)。为避免 运行 在 创建 别名时与已创建的 table 实例出现相同的问题,您很可能还应该在 之后清除注册表.
基本示例:
ConnectionManager::alias('energy', 'default');
$this->getTableLocator()->clear();
// table instances created after this point will respect the alias
$this->Users = null;
$this->loadModel('Users');
$energy = $this->Users->find()->first();
ConnectionManager::dropAlias('default');
$this->getTableLocator()->clear();
// table instances created after this point will use the non-aliased connection
$this->Users = null;
$this->loadModel('Users');
$gas = $this->Users->find()->first();
如您所见,这不仅需要再次加载 model/table,还需要取消设置 属性 上的实例,否则 loadModel()
将不会实际加载 table,而是 return 已经在 $this->Users
属性.
我建议你切换到通过 table 定位器获取你的模型,即使在你的控制器中,这样你就可以跳过这个尴尬的未设置的东西,并且你可以减少意外访问存储的旧实例的风险在 $this->Users
:
ConnectionManager::alias('energy', 'default');
$this->getTableLocator()->clear();
$energy = $this->getTableLocator()-get('Users')->find()->first();
ConnectionManager::dropAlias('default');
$this->getTableLocator()->clear();
$gas = $this->getTableLocator()-get('Users')->find()->first();
您可能还应该考虑将此连接切换集中在某个服务中。