web-dev-qa-db-fra.com

Doctrine2 - Insertion multiple en un coup

Je suis nouveau dans Doctrine et il y a encore des zones floues pour moi. Dans ce cas, j'insère un nouvel enregistrement dans la base de données à l'aide d'une boucle et du gestionnaire d'entités. Cela fonctionne bien, mais j'ai remarqué que Doctrine faisait une requête d'insertion par entité, ce qui peut devenir énorme.

En utilisant Doctrine2 et Symfony 2.3, je voudrais savoir comment nous pouvons le configurer pour qu’il ne fasse qu’une seule requête avec toutes les valeurs qu’il contient (nous ne parlons que d’une seule entité).

Ce que je veux dire change ceci:

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

Dans

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

Voici mon code:

$em = $this->container->get('doctrine')->getManager();

foreach($items as $item){
    $newItem = new Product($item['datas']);
    $em->persist($newItem);
}

$em->flush();
20
Molkobain

Selon cette réponse , Doctrine2 ne vous permet pas de combiner plusieurs instructions INSERT en une seule:

Certaines personnes semblent se demander pourquoi Doctrine n'utilise pas multi-insertions (insérer dans (...) des valeurs (...), (...), (...), ...

Tout d'abord, cette syntaxe n'est supportée que par mysql et les plus récents versions postgresql. Deuxièmement, il n’existe pas de moyen facile de mettre la main sur tous les identifiants générés dans un tel multi-insert lors de l'utilisation de AUTO_INCREMENT ou SERIAL et un ORM ont besoin des identifiants d'identité gestion des objets. Enfin, la performance d’insertion est rarement le goulot d'étranglement d'un ORM. Les inserts normaux sont plus que suffisamment rapides pour la plupart des situations et si vous voulez vraiment faire des insertions rapides en vrac, alors un Le multi-insert n’est pas le meilleur moyen, c’est-à-dire Postgres COPY ou Mysql LOAD DATA INFILE est plusieurs ordres de grandeur plus rapide.

Ce sont les raisons pour lesquelles il ne vaut pas la peine de mettre en œuvre un abstraction qui effectue des multi-insertions sur mysql et postgresql dans un fichier ORM.

Vous pouvez en savoir plus sur le traitement par lots de Doctrine2 ici: http://www.doctrine-project.org/blog/doctrine2-batch-processing.html

Vous pouvez passer à DBAL ou traiter vos données par petits lots en vidant votre gestionnaire d'entités après un nombre d'insertions défini:

$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();
38
ukliviu

Vous pouvez essayer cette fourche https://github.com/stas29a/doctrine2 . Il implémente exactement ce que vous voulez. Je l'ai testé avec MySQL et cela fonctionne bien et 5 fois plus rapidement que le traitement par lots. Cette fourchette obtient un premier identifiant inséré et l'incrémente en php pour obtenir d'autres identifiants. Cela fonctionne dans la plupart des cas, mais pas dans tous. Vous devez donc comprendre ce que vous faites lorsque vous utilisez cette fourchette.

2
s29a

Vous pouvez utiliser la méthode executeUpdate($query, array $params = array(), array $types = array()) de l'interface DriverConnection pour effectuer cette action. Cependant, il est peu difficile de lier plusieurs paramètres.

Les données:

$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éthode de mise à jour en masse:

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
    );
}
0
Sviat