如何使用多个实体管理器和连接
你可以在 Symfony 应用中使用多个 Doctrine 实体管理器或连接。如果你正在使用不同的数据库,甚至是具有完全不同实体集合的供应商,这是非常有必要的。换句话说,一个连接到一个数据库的实体管理器将处理一些实体,而另一个连接到另一个数据库的实体管理器可能会处理其余的实体。也可以使用多个实体管理器来管理一组通用的实体,每个实体管理器都有自己的数据库连接字符串或单独的缓存配置。
注意
配置多个实体管理器并不复杂,但它更高级,通常不是必需的。在添加此复杂性层之前,请确保你确实需要多个实体管理器。
警告
实体不能跨不同的实体管理器定义关联。如果你需要这样做,有一些替代方案,它们需要一些自定义设置。
以下配置代码展示了如何配置两个实体管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
# config/packages/doctrine.yaml
doctrine:
dbal:
connections:
default:
url: '%env(resolve:DATABASE_URL)%'
customer:
url: '%env(resolve:CUSTOMER_DATABASE_URL)%'
default_connection: default
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
Main:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity/Main'
prefix: 'App\Entity\Main'
alias: Main
customer:
connection: customer
mappings:
Customer:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity/Customer'
prefix: 'App\Entity\Customer'
alias: Customer
在这种情况下,你定义了两个实体管理器,分别命名为 default
和 customer
。default
实体管理器管理 src/Entity/Main
目录中的实体,而 customer
实体管理器管理 src/Entity/Customer
中的实体。你还定义了两个连接,每个实体管理器一个,但是你可以自由地为两者定义相同的连接。
警告
当使用多个连接和实体管理器时,你应该明确指定你想要的配置。如果你确实省略了连接或实体管理器的名称,则会使用默认值(即 default
)。
如果你为默认实体管理器使用了除 default
之外的其他名称,你将需要在 prod
环境配置和 Doctrine migrations 配置(如果你使用的话)中重新定义默认实体管理器
1 2 3 4 5 6
# config/packages/prod/doctrine.yaml
doctrine:
orm:
default_entity_manager: 'your default entity manager name'
# ...
1 2 3 4
# config/packages/doctrine_migrations.yaml
doctrine_migrations:
# ...
em: 'your default entity manager name'
当使用多个连接来创建数据库时
1 2 3 4 5
# Play only with "default" connection
$ php bin/console doctrine:database:create
# Play only with "customer" connection
$ php bin/console doctrine:database:create --connection=customer
当使用多个实体管理器来生成迁移时
1 2 3 4 5 6 7
# Play only with "default" mappings
$ php bin/console doctrine:migrations:diff
$ php bin/console doctrine:migrations:migrate
# Play only with "customer" mappings
$ php bin/console doctrine:migrations:diff --em=customer
$ php bin/console doctrine:migrations:migrate --em=customer
如果你在请求实体管理器时确实省略了实体管理器的名称,则会返回默认实体管理器(即 default
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// src/Controller/UserController.php
namespace App\Controller;
// ...
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
class UserController extends AbstractController
{
public function index(ManagerRegistry $doctrine): Response
{
// Both methods return the default entity manager
$entityManager = $doctrine->getManager();
$entityManager = $doctrine->getManager('default');
// This method returns instead the "customer" entity manager
$customerEntityManager = $doctrine->getManager('customer');
// ...
}
}
当使用 framework bundle 时,实体管理器也受益于 自动装配别名。例如,要注入 customer
实体管理器,请使用 EntityManagerInterface $customerEntityManager
类型提示你的方法。
你现在可以像以前一样使用 Doctrine - 使用 default
实体管理器来持久化和获取它管理的实体,并使用 customer
实体管理器来持久化和获取它的实体。
同样适用于仓库调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// src/Controller/UserController.php
namespace App\Controller;
use AcmeStoreBundle\Entity\Customer;
use AcmeStoreBundle\Entity\Product;
use Doctrine\Persistence\ManagerRegistry;
// ...
class UserController extends AbstractController
{
public function index(ManagerRegistry $doctrine): Response
{
// Retrieves a repository managed by the "default" entity manager
$products = $doctrine->getRepository(Product::class)->findAll();
// Explicit way to deal with the "default" entity manager
$products = $doctrine->getRepository(Product::class, 'default')->findAll();
// Retrieves a repository managed by the "customer" entity manager
$customers = $doctrine->getRepository(Customer::class, 'customer')->findAll();
// ...
}
}
警告
一个实体可以由多个实体管理器管理。但是,当在你的自定义仓库中从 ServiceEntityRepository
扩展时,这会导致意外的行为。ServiceEntityRepository
始终使用为该实体配置的实体管理器。
为了解决这种情况,请扩展 EntityRepository
而不是再依赖自动装配
1 2 3 4 5 6 7 8 9
// src/Repository/CustomerRepository.php
namespace App\Repository;
use Doctrine\ORM\EntityRepository;
class CustomerRepository extends EntityRepository
{
// ...
}
现在你应该始终使用 ManagerRegistry::getRepository()
获取此仓库。