Doctrine 实体删除 vs 删除查询,性能比较

2022-01-16 00:00:00 performance php doctrine

在使用原则时,我注意到,要删除一个实体,我需要通过给定的参数(名称、id 等)检索该实体,然后调用 remove 方法.另一方面,在查询中,我可以只执行删除查询.

While using doctrine, I noticed that, to delete an entity, I need to retrieve that entity by given parameter(name,id etc) and then call the remove method. On the other hand, in query, I can just execute delete query.

所以,看起来,使用 ORM 样式需要两次操作,而一般的 sql 操作需要一次操作.这就是为什么,我有点困惑,我们是否应该在 ORM 中使用删除(或更新)操作?性能不是更差吗?或者还有什么我想念的吗?可以用 ORM 风格以任何其他方式完成吗?

So, seems like, using ORM style requires two operation and general sql operation require one operation. That's why, I am a little confusing, whether we should use delete(or update) operation in ORM? Isn't it worse in performance? Or Is there anything else I am missing? Can it be done in any other way in ORM style?

推荐答案

在 Doctrine2 中,您可以对未从数据库加载的代理对象调用 delete.只需创建一个虚拟"对象,例如:

In Doctrine2 you can call the delete on a proxy object, which is not loaded from the database. Just create a "dummy" object, something like:

$user = $em->getPartialReference('modelUser', array('id' => $id));
$em->remove($user);

它不需要初始查询,但我不太确定 Doctrine 是否仍然在 flush 内部执行此操作.我在 SqlLog 中没有看到它.

It doesn't require the initial query, but I'm not quite sure if Doctrine still does it internally on flush. I don't see it in the SqlLog.

补充一点,我认为这是任何体面的 ORM 的预期行为.它处理对象和关系.它必须在删除之前知道某些东西的存在.ORM 不仅仅是一个查询生成器.通常,原生查询在任何 ORM 中总是更快.任何 ORM 都会增加一个抽象层,执行它需要一些时间.这是一个典型的权衡,你会得到一些花哨的功能和干净的代码,但会失去一些性能.

Just to add, I think this is expected behavior of any decent ORM. It deals with objects and relations. It has to know that something exists before deleting it. ORM is not just a query generator. Generally, a native query will always be faster in any ORM. Any ORM adds a layer of abstraction and it takes some time to execute it. It is a typical tradeoff, you get some fancy features and clean code, but loose some on performance.

我很高兴它对你有用.实际上我偶然发现了另一个问题,这让我意识到代理和部分对象实际上并不是一回事.部分对象实例化真实的模型类,并用您想要的值填充它.初始化部分对象后,延迟加载不再适用.因此,例如,如果您创建一个只有 id 的部分对象,并且仅在另一个对象字段满足某些条件时才想删除,它将不起作用,因为该其他字段将始终为空.

I'm glad it worked out for you. Actually I stumbled on another problem, which made me realize that proxies and partial objects aren't actually the same thing. Partial objects instance the real model class, and fill it with values you want. After you initialize a partial object lazy-loading doesn't work on it anymore. So for instance, if you make a partial object with only the id, and want to delete only if another object field satisfies some condition, it will not work, because that other field will always be null.

另一方面,代理确实可以使用延迟加载,并且不会共享部分对象存在的问题.所以我强烈建议不要使用 getPartialReference 方法,而是你可以这样做:

On the other hand, proxies do work with lazy-loading, and don't share the problems that partial objects have. So I would strongly suggest not to use getPartialReference method, instead you can do something like:

$user = $em->getReference('modelUser', $id);
$em->remove($user);

getReference 方法如果对象已经加载则返回对象,否则返回代理.如果/当您需要它们时,代理可以延迟加载所有其他值.至于您的示例,它们的行为将完全相同,但代理肯定是更好的选择.

The getReference method returns the object if it is already loaded or a proxy if it is not. A proxy can lazy-load all the other values if/when you need them. As for your example, they will behave exactly the same, but proxies are surely a better way to go.

相关文章