问题
我有一个业务场景,管理员需要导入一个中等规模的参与者列表,最多10,000条记录。实体在4个字段(姓名、姓氏、电子邮件和事件ID)上具有唯一约束条件。更新或插入新记录到数据库的最佳方法是什么?
可能的情况是,列表只被导入一次,然后更多的参与者被附加到Excel文件中,也许一些额外的非唯一字段在现有参与者上被改变,然后文件再次被导入。
我尝试过的方法
最初的想法是使用Doctrine的
merge()
函数,但这会导致约束违规,我的猜测是因为我的对象不包含一个id第二种方法是在每次迭代时刷新并捕获异常,但这会导致实体管理器关闭。我尝试重置它,但无效。可能可以尝试持久化不抛出异常的实体,并在引发异常的实体上进行另一次迭代,但这意味着发送10,000个查询
另一个想法是选择所有现有参与者,将它们放入数组集合中,并在每次迭代时调用
exists()
或filter()
,但这会导致过滤函数在每个集合元素上被调用,所以最终会有10,000 x 10,000 = 100,000,000次检查,性能非常差另一个想法是获取现有参与者并连接唯一字段,将它们放入哈希映射中,理论上这可能会起作用
我的最后一个想法是尝试以100的块插入所有内容。如果插入成功,我们移动到另一个100的块。如果块抛出约束违规,我们将块分成10个小块,并尝试做同样的事情。只是不知道这在最坏情况下的表现如何
我的代码
实体:
class Person implements Comparable
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="surname", type="string", length=255)
*/
private $surname;
/**
* @var string
*
* @ORM\Column(name="email", type="string", length=255)
*/
private $email;
/**
* @var object
*
* @ORM\Column(name="extras", type="object")
*/
private $extras;
/**
* @ORM\ManyToOne(targetEntity="Event")
* @ORM\JoinColumn(name="event_id", referencedColumnName="id", nullable=false)
*/
private $event;
<...>
public function compareTo($other)
{
if(get_class($other) == Person::class)
{
if($other->getName() == $this->name
&& $other->getSurname() == $this->surname
&& $other->getEmail() == $this->email
&& $other->getEvent() == $this->event)
{
return true;
}
}
return false;
}
}
控制器:
public function postTicketsAction(Request $request, Event $event)
{
<...>
if($form->isValid())
{
<...>
while($iterator->valid())
{
<...>
$person = new Person();
$person
->setEvent($event)
->setName($name)
->setSurname($surname)
->setEmail($email)
->setExtras($extras)
;
$em->merge($person);
<...>
}
dump($failedPeople);
$em->flush();
}
}