批处理使用Doctrine比看起来要棘手,即使有
iterate()
和
IterableResult
的帮助。正如您所期望的那样,
IterableResult
的最大好处是它不会将所有元素加载到内存中,第二个好处是它不会保存对您加载的实体的引用,因此
IterableResult
不会阻止GC从您的实体中释放内存。但是,还有另一个对象Doctrine的
EntityManager
(更具体地说是
UnitOfWork
),它保存了对显式或隐式查询的每个对象的所有引用(
EAGER
关联)。简而言之,无论您通过
findAll()
、
findOneBy()
甚至是
DQL
查询和
IterableResult
返回任何实体时,都会在Doctrine内部保存对每个实体的引用。该引用仅存储在一个关联数组中,以下是伪代码:
$identityMap['Acme\Entities\Image'][0] = $image0;
。
因为在每次循环迭代时,即使你先前的图像不在循环范围或IterableResult的范围内,它们仍然存在于identityMap中,GC无法清除它们,因此你的内存消耗与调用findAll()时相同。
现在让我们看看代码实际发生了什么。
$query = $this->em->createQuery('SELECT i FROM Acme\Entities\Image i');
$iterable = $query->iterate();
while (($image_row = $iterable->next()) !== false) {
$image = $image_row[0];
write_image_data_to_file($image,'myimage.data.bin');
}
所以第一种解决方案是实际告诉Doctrine EntityManager将对象从$identityMap中分离。我还将while循环替换为foreach,使其更易读。
foreach($iterable as $image_row){
$image = $image_row[0];
write_image_data_to_file($image);
$entity_manager->detach($image);
}
然而,上面的示例存在一些缺陷,即使它被列在
Doctrine批处理文档中。如果您的实体
Image
中有任何一个关联是
EAGER
加载,那么它就无法正常工作。但是,如果您EAGER地加载了任何一个关联,例如:
class Image {
private $id;
private $imageName;
private $owner;
}
如果我们使用与上面相同的代码段,则在
foreach($iterable as $image_row){
$image = $image_row[0];
write_image_data_to_file($image);
$entity_manager->detach($image);
}
可能的解决方案是使用
EntityManager::clear()
或
EntityManager::detach()
,它们可以完全清除身份映射表。
foreach($iterable as $image_row){
$image = $image_row[0];
write_image_data_to_file($image);
$entity_manager->clear();
}
$image->getUser()->getProfession()->getSalary()
那样跟随一个对象到另一个对象的关系。它们将被返回为大型联合数组(类似于SELECT ... JOIN
)。当你拥有平面对象关系(1或2级)时,HYDRATE_SCALAR可能适用于你。但我不确定当你稍后向对象添加更多关系时它是如何工作的... - Dimitry K->iterate()
方法只会返回一个包装对象(实际上还没有向MySQL或MySQL驱动程序发出任何物理请求)。只有在迭代器请求第一个项目时,才会启动查询过程。(因此,这种行为不会与@BlocksByLukas所述的相矛盾,假设他的研究结果是正确的,并且适用于此处)。另外,在我之前的评论中,我错误地提到了两个方法getIterator()/ iterate()
,但应该正确地只有一个iterate()
(无法再编辑该评论)。 - Dimitry KORM工具不适用于大规模插入、更新或删除。每个RDBMS都有自己最有效的处理这些操作的方式......
看起来答案是:"不要使用Doctrine进行批处理" :S 你也可以在这里找到Doctrine Issue on Deprecation of EntityRepository#clear() 有帮助。 - Dimitry K