Doctrine: ON DUPLICATE KEY UPDATE

2019-04-19 05:00发布

How can I write an INSERT doctrine query with option ON DUPLICATE KEY UPDATE?

9条回答
手持菜刀,她持情操
2楼-- · 2019-04-19 05:40

I had the same problem and after investigating a bit it looks like Doctrine doesn't do it. My solution was to do a findBy before my insert to see if any records exist with the unique fields. If this returns an entity then I update that entity and persist it instead of creating a new entity to persist.

If you are concerned about performance then this is not ideal as we are doing a select before every insert. However since Doctrine is database agnostic it is the only alternative to locking yourself to MySQL. It's one of those tradeoffs: do you want performance or portability.

查看更多
孤傲高冷的网名
3楼-- · 2019-04-19 05:41

for Symfony 2 use raw sql:

$em->getConnection()->prepare("INSERT INTO table SET 
    some_fields = "some data", created_at = NOW() 
    ON DUPLICATE KEY UPDATE
    some_fields = "some data", updated_at = NOW()
")->execute();
查看更多
冷血范
4楼-- · 2019-04-19 05:53

You can't. It's not supported by Doctrine right now.

What you could do is to imitate what MySQL does by checking if the entity exists and update/create it accordingly:

$em = $this->getEntityManager();

// Prevent race conditions by putting this into a transaction.
$em->transactional(function($em) use ($content, $type) {
  // Use pessimistic write lock when selecting.
  $counter = $em->createQueryBuilder()
    ->select('MyBundle:MyCounter', 'c')
    ->where('c.content = :content', 'c.type = :type')
    ->setParameters(['content' => $content, 'type' => $type])
    ->setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE);
    ->getQuery()
    ->getResult()
  ;

  // Update if existing.
  if ($counter) {
    $counter->increase();
  } else {
    // Create otherwise.
    $newCounter = new Counter($content, $type, 1);
    $em->persist($newCounter);
  }
});

If the record exists PESSIMISTIC_WRITE makes sure that it's not updated by anyone (e.g., other threads) while we're updating it.

Although you need to check for the entity's existence on every update, it's a simple reproduction of "update if existing and create if not".

As pointed out in the comments this does not prevent a race condition if the record doesn't exist: If a row with the same key(s) gets inserted between the select and the insert you're running into a duplicate key exception.

But given the constraints that this needs to be DB independent and thus written using Doctrine and not using native SQL it may help in some cases.

References:

查看更多
登录 后发表回答