php - generate - symfony 3.4 database
Doctrine2-inserción múltiple en un disparo (4)
Soy nuevo en Doctrine y todavía hay algunas áreas borrosas para mí. En este caso, estoy insertando un nuevo registro en la base de datos utilizando un bucle y el administrador de entidades. Funciona bien, pero me di cuenta de que Doctrine hace una consulta de inserción por entidad, lo que puede llegar a ser bastante grande.
Usando Doctrine2 y Symfony 2.3, me gustaría saber cómo podemos configurarlo para que haga solo 1 consulta de inserción con todos los valores (estamos hablando de 1 entidad solo por supuesto)
Lo que quiero decir es cambiar esto:
INSERT INTO dummy_table VALUES (x1, y1)
INSERT INTO dummy_table VALUES (x2, y2)
Dentro
INSERT INTO dummy_table VALUES (x1, y1), (x2, y2)
Aquí está mi código:
$em = $this->container->get(''doctrine'')->getManager();
foreach($items as $item){
$newItem = new Product($item[''datas'']);
$em->persist($newItem);
}
$em->flush();
De acuerdo con esta respuesta , Doctrine2 no le permite combinar varias declaraciones INSERT en una:
Algunas personas parecen estar preguntándose por qué Doctrine no usa inserciones múltiples (inserte en (...) valores (...), (...), (...), ...
En primer lugar, esta sintaxis solo es compatible con las versiones mysql y postgresql más nuevas. En segundo lugar, no hay una manera fácil de obtener todos los identificadores generados en una inserción múltiple de este tipo cuando se utiliza AUTO_INCREMENT o SERIAL y un ORM necesita los identificadores para la gestión de identidad de los objetos. Por último, el rendimiento del inserto rara vez es el cuello de botella de un ORM. Las inserciones normales son más que lo suficientemente rápidas para la mayoría de las situaciones y si realmente desea hacer inserciones masivas rápidas, entonces una inserción múltiple no es la mejor manera de todos modos, es decir, COPY de Postgres o Mysql LOAD INFILE DATOS son varios órdenes de magnitud más rápidos.
Estas son las razones por las que no vale la pena el esfuerzo de implementar una abstracción que realice inserciones múltiples en mysql y postgresql en un ORM.
Puede leer más sobre el procesamiento por lotes de Doctrine2 aquí: http://www.doctrine-project.org/blog/doctrine2-batch-processing.html
Puede cambiar a DBAL o recurrir al procesamiento de sus datos en pequeños lotes vaciando a su administrador de entidades después de una cantidad determinada de inserciones:
$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();
No lo he probado, pero parece posible hacerlo con una colección.
$collection = new Doctrine_Collection(''tablename'');
$collection->add($record1);
$collection->add($record2);
$collection->add($record3);
$collection->add($record4);
$collection->save();
Por supuesto que debe tener el complemento en el bucle.
Puede probar este tenedor https://github.com/stas29a/doctrine2 . Implementa exactamente lo que quieres. Lo probé en MySQL y funciona bien y 5 veces más rápido que el procesamiento por lotes. Este tenedor obtiene una primera identificación insertada y la incrementa en php para obtener otras identificaciones. Funciona para la mayoría de los casos pero no en todos. Así que necesitas entender qué estás haciendo cuando usas este tenedor.
Puede usar el executeUpdate($query, array $params = array(), array $types = array())
de la interfaz DriverConnection
para realizar esta acción. Sin embargo, es poco complicado vincular varios parámetros.
Datos:
$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'',
]
];
Método de actualización masiva:
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
);
}