持久化实体而不将其附加到 EntityManager

Posted

技术标签:

【中文标题】持久化实体而不将其附加到 EntityManager【英文标题】:Persist an entity without attaching it to the EntityManager 【发布时间】:2016-04-09 10:07:18 【问题描述】:

我想从 XML 文件中批量导入 Doctrine 实体。

XML 文件可能非常大(最多 100 万个实体),因此我无法以传统方式持久化所有实体:

$em->beginTransaction();

while ($entity = $xmlReader->readNextEntity()) 
    $em->persist($entity);


$em->flush();
$em->commit();

我很快就会超过我的内存限制,而 Doctrine 并不是真正设计用于处理那么多托管实体。

我不需要跟踪对持久化实体的更改,只是为了持久化它们;因此我不希望它们由 EntityManager 管理。

是否可以在不由 EntityManager 管理的情况下保留实体?


我想到的第一个选项是在持久化后立即将其分离:

$em->beginTransaction();

while ($entity = $xmlReader->readNextEntity()) 
    $em->persist($entity);
    $em->flush($entity);
    $em->detach($entity);


$em->commit();

但这在 Doctrine 中相当昂贵,并且会减慢导入速度。


另一种选择是使用Connection 对象和准备好的语句直接将数据插入数据库,但我喜欢实体的抽象,理想情况下希望存储对象直接。

【问题讨论】:

【参考方案1】:

您可以在每次插入后调用clear(将所有实体与管理器分离)和flush,而不是在每次插入后使用detachflush,这应该会明显更快:

Doctrine 中的批量插入最好分批执行,取 事务性后写行为的优势 实体管理器。以下代码显示了插入 10000 的示例 批量大小为 20 的对象。您可能需要尝试使用 批量大小以找到最适合您的大小。大批量 大小意味着更多的准备好的语句在内部重用,但也意味着更多 在冲洗期间工作。

https://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html

如果可能,我建议避免批量操作的事务,因为它们往往会减慢速度:

//$em->beginTransaction();
$i = 0;

while ($entity = $xmlReader->readNextEntity()) 
    $em->persist($entity);
    if(++$i % 20 == 0) 
        $em->flush();
        $em->clear(); // detaches all entities
    


$em->flush(); //Persist objects that did not make up an entire batch
$em->clear();

//$em->commit();

【讨论】:

我希望让 EntityManager 保持当前状态,但这毕竟不是什么大问题。我还看到clear() 接受一个可选的类名来仅分离给定类的实体,这在这种情况下会很有帮助。所以我想这是目前唯一的方法!

以上是关于持久化实体而不将其附加到 EntityManager的主要内容,如果未能解决你的问题,请参考以下文章

如何直接保存到持久存储,而不将数据保存到内存中

谷歌分析跨域跟踪而不将 Cookie 附加到 URL?

捕获图像而不将其存储到内部/外部存储

直接将文件上传到SFTP而不将其存储到本地系统[重复]

生成pdf而不将其保存到磁盘然后将其显示给浏览器

Android:旋转图像而不将其加载到内存中