我的问题可能是this one的重复,但我找不到任何令人满意的答案,所以我会尝试使这个更精确。
我正在从其他API构建导入服务。我不想在我的新数据库中有任何重复。
所以这是我当前实现的一个例子:
控制器:
public function mainAction ()
{
$em = $this->getDoctrine()->getManager();
$persons_data = [
[
'first_name' => 'John',
'last_name' => 'Doe'
],
[
'first_name' => 'John',
'last_name' => 'Doe'
]
];
$array = [];
foreach($persons_data as $person_data)
{
$person = $this->get('my_service')->findOrCreatePerson($person_data);
$array[] = $person;
}
$em->flush();
return new Response();
}
服务功能:
public function findOrCreatePerson ($data)
{
$em = $this->em;
$person = $em->getRepository('AppBundle:Person')->findOneBy([
'first_name' => $data['first_name'],
'last_name' => $data['last_name']
]);
if(is_null($person)) {
$person = new Person();
$person->setFirstName($data['first_name']);
$person->setLastName($data['last_name']);
$em->persist($person);
}
return $person
}
我尽量让它尽可能简单。
正如您所看到的,我想只进行一次数据库事务以获得一些性能改进。
问题是,如果我在findOrCreatePerson()
方法的末尾不刷新,则对Person
存储库的查询将找不到第一个对象,并将在数据库中创建重复项。
我的问题很简单:我该如何实施这样的事情?
答案 0 :(得分:3)
这是memoize的工作!
// Cache
private $persons = [];
public function findOrCreatePerson ($data)
{
// Need unique identifier for persons
$personKey = $data['first_name'] . $data['last_name'];
// Already processed ?
if (isset($this->persons[$personKey])) {
return $this->persons[$personKey];
}
$em = $this->em;
$person = $em->getRepository('AppBundle:Person')->findOneBy([
'first_name' => $data['first_name'],
'last_name' => $data['last_name']
]);
if(is_null($person)) {
$person = new Person();
$person->setFirstName($data['first_name']);
$person->setLastName($data['last_name']);
$em->persist($person);
}
// Cache
$this->persons[$personKey] = $person;
return $person
}
答案 1 :(得分:2)
Cerad的回答(备忘录)很好,但我鼓励你重新考虑一些事情。
正如您所看到的,我只想进行一次数据库事务以获得一些性能改进。
这句话有些不对劲。
主要的一点是,您将flush()与单个原子事务进行混淆。您可以手动管理交易边界,这样做通常非常有利。
第二件事是,当你谈论批量导入时,你会很快发现你遇到的第一个性能问题根本就不是数据库。它是EntityManager本身。随着EM的内部身份地图变得膨胀,持续存在于DB的计算变化变得非常非常缓慢。
我考虑按如下方式重写你的核心循环,看看它是否足够快。只有在必要时才考虑备忘。
$em->beginTransaction();
foreach($persons_data as $person_data)
{
$person = $this->get('my_service')->findOrCreatePerson($person_data);
$em->flush();
$em->clear(); // don't keep previously inserted entities in the EM.
}
$em->commit();