1

假设我有以下查询,它返回一个带有已标记的关联 Post 实体的 User 实体(这仅用于演示):

SELECT u, p
FROM User u
LEFT JOIN u.posts p
WHERE p.isFlagged = true

一旦我使用了它,我就想访问该用户的所有帖子,无论 isFlagged 是什么。是否有一种简单的方法来刷新$user->posts集合,使其成为所有用户帖子的完整集合?

我不想只提取查询中的所有帖子(因为它们可能不需要),并且需要完整集合的代码不会知道该实体来自的查询。

4

3 回答 3

1

你在这里做的事情被打破了:

SELECT
    u, p
FROM
    User u
JOIN
    u.posts p
    WITH
    p.isFlagged = true

这将使用错误的值对“User#posts”集合进行水合,从而导致逻辑损坏、对象图损坏、一切都损坏。

您应该在 DQL 级别使用以下方法解决此问题:

SELECT
    u, p
FROM
    User u
JOIN
    u.posts p
JOIN
    u.posts j
    WITH
    j.isFlagged = true

这将基本上水合用户对象中正确的帖子集合,而没有任何中间(损坏)状态。

编辑:我误解了这个问题,因为我的想法是基于@Athlan 的回答,这从根本上是错误的(刷新损坏的集合真的是错误的)。这是我之前的答案,我仍然认为它很有趣,因为它解决了问题,但不是以真正正确的方式。

这个问题实际上让我很好奇 ORM 是否像我预期的那样刷新了集合。

在这个分支中编写了测试

基本上,您需要做的只是:

$entityManager->refresh($entity);

这是测试的相关部分:

$foo = new DDC2666Foo();

$this->_em->persist($foo);
$this->_em->flush();
$this->_em->clear();

$fetchedFoo = $this->_em->find(__NAMESPACE__ . '\DDC2666Foo', $foo->id);

$fetchedFoo->bars->add(new DDC2666Bar());

$this->assertCount(1, $fetchedFoo->bars);
$this->_em->refresh($fetchedFoo);

$this->assertCount(0, $fetchedFoo->bars, 'The collection was reset');

唯一的缺点是这也会重新获取您的实体,但 ORM 本身不提供刷新单个集合的外观。

无论如何,这也是一件好事,因为这样,您不会破坏封装,这可能会导致代码中出现意外(且难以调试)行为。

于 2013-03-24T16:43:35.383 回答
0

我遇到了同样的问题,最终来到了这里。刷新对我来说不是一个选项,因为我有一个列表并且我不想进行这些额外的查询。

您在这里真正需要的是添加一个“假”连接来进行过滤,并让原始连接没有条件......以明确这一点:

SELECT u, p
FROM User u
LEFT JOIN u.posts p
LEFT JOIN u.posts p2
WHERE p2.isFlagged = true

请注意,我将 p2 从 select 子句中排除,这只是为了过滤目的。

于 2014-06-25T20:26:31.440 回答
0

我发现其他解决方案非常不令人满意,因为许多实体会得到我根本不感兴趣的水分。如果绝大多数帖子都有,isFlagged = 0那么性能影响是巨大的。

这是一个替代解决方案,涉及交换实体之间的关系。但是,它并不完全等同于原始问题的要求。然而,它可能对遇到此问题的其他人有用:

SELECT p, u
FROM Post p
JOIN p.user u
WHERE p.isFlagged = true

此解决方案与其他解决方案之间的区别在于,它将不再返回没有任何帖子或任何已标记帖子的用户。这可能是也可能不是您想要的。

于 2019-06-04T13:05:42.380 回答