Doctrine2 – множественная вставка одним выстрелом

Я новичок в Доктрине, и для меня все еще есть некоторые размытые области. В этом случае я вставляю новую запись в базу данных с помощью цикла и менеджера сущностей. Он отлично работает, но я заметил, что Doctrine делает один запрос на вставку сущностью, которая может стать довольно огромной.

Используя Doctrine2 и Symfony 2.3, я хотел бы знать, как мы можем настроить его, чтобы он сделал только 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 в один:

Некоторые люди, похоже, задаются вопросом, почему Doctrine не использует мульти-вставки (вставлять в (…) значения (…), (…), (…), …

Прежде всего, этот синтаксис поддерживается только в mysql и более поздних версиях postgresql. Во-вторых, нет простого способа получить все сгенерированные идентификаторы в такой мультиинсталляции при использовании AUTO_INCREMENT или SERIAL, а ORM нужны идентификаторы для управления идентификацией объектов. Наконец, вставка производительности редко является узким местом ORM. Обычные вставки более чем достаточно быстры для большинства ситуаций, и если вы действительно хотите делать быстрые вставки вставки, то мульти-вставка не является наилучшим способом, то есть Postgres COPY или Mysql LOAD DATA INFILE на несколько порядков быстрее.

Это причины, по которым не стоит пытаться реализовать абстракцию, которая выполняет множественные вставки в mysql и postgresql в ORM.

Вы можете узнать больше о пакетной обработке Doctrine2 здесь: http://www.doctrine-project.org/blog/doctrine2-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 для получения других идентификаторов. Он работает в большинстве случаев, но не во всех. Поэтому вам нужно понять, что вы делаете при использовании этой вилки.

Для выполнения этого действия вы можете использовать executeUpdate($query, array $params = array(), array $types = array()) интерфейса DriverConnection . Однако сложно связать несколько параметров.

Данные:

 $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 ); } 

Я не тестировал его, но это возможно сделать с помощью коллекции.

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

Конечно, вы должны добавить в цикл.