原则:关于重复密钥更新
如何使用选项ON DUPLICATE KEY UPDATE
编写INSERT
学说查询?
How can I write an INSERT
doctrine query with option ON DUPLICATE KEY UPDATE
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
对于 Symfony 2
使用原始sql:
for Symfony 2
use raw sql:
问题是这是 MySQL 特定的问题,因此 Doctrine 不会直接涵盖它。
正如评论中提到的,您需要为此编写一个 RawSQL 查询。这将是最简单的方法。
如果您希望它更复杂并且真正独立于数据库,请查看 事件及其可能性。在执行实际查询之前,您可以检查是否存在,如果存在,则采取相应的操作。
一种独立于 ORM/PHP 的方法是编写一个存储过程/触发器来处理数据库端的这个问题。
The problem is that this is a MySQL specific problem so it will not be directly covered by Doctrine.
As a comment mentioned, you would need to write a RawSQL Query for this. This would be the easiest way.
If you want it more sophisticated and truely DB independent, look into Events and it's possibilities. Before the actual query is executed, you can check for an existence and if it exists, act accordingly.
An ORM/PHP independent way is to write a stored procedure/trigger that handles this problem database side.
你不能。目前 Doctrine 不支持它。
您可以做的是通过检查实体是否存在并相应地更新/创建它来模仿 MySQL 的操作:
如果记录存在,PESSIMISTIC_WRITE 确保它不会被任何人(例如其他线程)更新,而我们正在更新它。
尽管您需要在每次更新时检查实体是否存在,但这只是“如果存在则更新,如果不存在则创建”的简单再现。
正如评论中指出的如果记录不存在,这不会阻止竞争条件:如果在您运行的选择和插入之间插入具有相同键的行进入重复键异常。
但考虑到这需要独立于数据库的限制,因此使用 Doctrine 编写而不是使用本机 SQL,这在某些情况下可能会有所帮助。
参考资料:
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:
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:
您可以使用这样的函数来构建和执行原始 sql:
You can use function like this to build and execute raw sql:
我遇到了同样的问题,经过一番调查后,似乎 Doctrine 并没有做到这一点。我的解决方案是在插入之前执行 findBy 以查看是否存在具有唯一字段的任何记录。如果这返回一个实体,那么我会更新该实体并保留它,而不是创建一个新的实体来保留。
如果您担心性能,那么这并不理想,因为我们在每次插入之前都进行选择。然而,由于 Doctrine 与数据库无关,因此它是锁定 MySQL 的唯一选择。这是权衡之一:您想要性能还是便携性。
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.
我创建了一个学说 dbal 包装器来做到这一点。它可以与带有 dbalwrapper_class 选项的 DoctrineBundle 一起使用。
https://github.com/iJanki/doctrine-mysql-dbal-extensions
I created a doctrine dbal wrapper to do that. It can be used with DoctrineBundle with the dbal wrapper_class option.
https://github.com/iJanki/doctrine-mysql-dbal-extensions
您有三个选项。
第一个选项下拉至 SQL。然后您就可以使用您选择的 RDBMS 提供的所有功能。但除非绝对必要,否则许多程序员不愿意使用 SQL。
第二个选项是锁定另一个表中的相关行。例如,如果您要插入的实体对于每个用户都有一个唯一的密钥,您可以对要插入/更新实体的用户进行锁定。此解决方案的问题在于,它不适用于像
User
本身这样的根实体,因为您无法锁定尚不存在的行。第三选项是仅捕获重复键错误/异常。也就是说,您不检查具有特定键的行是否已经存在;相反,您只需尝试插入它。如果成功了,一切都好。如果由于重复键错误/异常而失败,您可以捕获它并更新现有行。此解决方案是最好的,因为它避免了每次插入之前的额外
SELECT
查询,而这对于满足竞争条件的可能性较低来说是一个恒定的开销。它是最好的,因为它适用于根实体和非根实体。You have three options.
The first option is to drop down to SQL. Then you can use all the features your RDBMS of choice provides. But many programmers don't want to drop down to SQL unless absolutely necessary.
The second option is to lock on a related row in another table. For instance, if the entity you're inserting has a unique key per user, you could do a lock on the user you're inserting/updating the entity for. The problem with this solution is that it doesn't work for root entities like
User
itself because you can't lock a row that doesn't exist yet.The third option is to just catch the duplicate key error/exception. That is, you don't check if a row with a particular key already exists; instead, you just attempt to insert it. If it succeeds, all is good. If it fails with the duplicate key error/exception, you catch it and update the existing row. This solution is the best because it avoids an extra
SELECT
query before each insertion that's a constant overhead for the low probability of hitting a race condition. And it's the best because it works for both root and non-root entities.如果这有帮助,您可以扩展查询生成器以附加任意 SQL(显然,这可能无法跨 PDO 引擎工作):
In case this helps, you can extend the query builder to append arbitrary SQL (obviously, this may not work across PDO engines):
我为我写了简单的解决方案。刚刚创建了 AbstractRepository 类,它是所有存储库的父类(例如 UserRepository)
并创建了下一个方法:
您可以像这样使用此代码:
I wrote simple solution for me. Just created AbstractRepository class which is parent class of all Repositories(for example UserRepository)
and created next method:
You can use this code like this: