原则2 - 一次多次插入


Doctrine2 - Multiple insert in one shot

我是教义的新手,对我来说还有一些模糊的地方。在本例中,我使用循环和实体管理器在数据库中插入新记录。它工作正常,但我注意到 Doctrine 按实体进行一个插入查询,这可能会变得非常庞大。

使用 Doctrine2 和 Symfony 2.3,我想知道如何设置它,以便它只进行 1 个包含所有值的插入查询(当然,我们只谈论 1 个实体)。

我的意思是改变这个:

INSERT INTO dummy_table VALUES (x1, y1)    
INSERT INTO dummy_table VALUES (x2, y2)

INSERT INTO dummy_table VALUES (x1, y1), (x2, y2)

这是我的代码:

$em = $this->container->get('doctrine')->getManager();
foreach($items as $item){
    $newItem = new Product($item['datas']);
    $em->persist($newItem);
}
$em->flush();

根据这个答案,Doctrine2 不允许您将多个 INSERT 语句组合成一个:

有些人似乎想知道为什么教义不使用多次插入(插入到 (...) 值 (...)、(...)、(...)、...

首先,此语法仅在mysql和更新版本上受支持PostgreSQL 版本。其次,没有简单的方法可以掌握所有使用时在此类多插入中生成的标识符AUTO_INCREMENT或串行和ORM需要身份标识符对象的管理。最后,刀片性能很少ORM的瓶颈。普通刀片的速度足够快大多数情况下,如果您真的想进行快速批量插入,那么无论如何,多插入不是最好的方法,即Postgres COPY或Mysql加载数据文件的速度快了几个数量级。

这些是为什么不值得努力实施在 MySQL 和 PostgreSQL 上执行多插入的抽象ORM。

您可以在此处阅读有关 Doctrine2 批处理的更多信息:https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/batch-processing.html

您可以切换到 DBAL,也可以通过在一定数量的插入后刷新实体管理器来小批量处理数据:

$batchSize = 20;
foreach ($items as $i => $item) {
     $product = new Product($item['datas']);
     $em->persist($product);
     // flush everything to the database every 20 inserts
     if (($i % $batchSize) == 0) {
         $em->flush();
         $em->clear();
    }
}
// flush the remaining objects
$em->flush();
$em->clear();
你可以

试试这个分叉 https://github.com/stas29a/doctrine2。它完全实现了您想要的。我在 MySQL 中对其进行了测试,它工作正常,比批处理快 5 倍。这个分支获取第一个插入的 id,并在 php 中递增它以获取其他 id。它适用于大多数情况,但并非全部。因此,您需要了解使用此分叉时在做什么。

您可以使用DriverConnection接口executeUpdate($query, array $params = array(), array $types = array())方法来执行此操作。但是,绑定多个参数有点棘手。

数据:

$postMetaData = [
    [
        'post_id' => $product->getId(),
        'meta_key' => '_visibility',
        'meta_value' => 'visible',
    ],
    [
        'post_id' => $product->getId(),
        'meta_key' => '_stock_status',
        'meta_value' => $insert['in_stock'] ? 'instock' : 'outofstock',
    ]
];

批量更新方法:

public function updateOrCreateBulk($posts, 'Doctrine'DBAL'Connection $connection)
{
    $placeholders = [];
    $values = [];
    $types = [];
    foreach ($posts as $columnName => $value) {
        $placeholders[] = '(?)';
        $values[] = array_values($value);
        $types[] = 'Doctrine'DBAL'Connection::PARAM_INT_ARRAY;
    }
    return $connection->executeUpdate(
        'INSERT INTO `wp_postmeta` (`post_id`, `meta_key`, `meta_value`)  VALUES ' . implode(', ', $placeholders) . ' ON DUPLICATE KEY UPDATE `meta_value` = VALUES(`meta_value`)',
        $values,
        $types
    );
}

谢谢,现在我知道这不可能通过教义。@Molkobain

您可以一次性使用

$em->merge($testCustomer);
然后保持刷新

它将工作完美

我还没有测试过它,但似乎可以用集合来做到这一点。

$collection = new Doctrine_Collection('tablename');
$collection->add($record1);
$collection->add($record2);
$collection->add($record3);
$collection->add($record4);
$collection->save();

当然,您应该在循环中添加。