如何强制Doctrine从数据库重新加载数据?

51

我在Symfony2.1中使用doctrine/mongodb 1.0.0-BETA1。我正在尝试强制我的存储库从数据库调用数据,而不是使用其缓存的对象。

$audit = $dm->getRepository("WGenSimschoolsBundle:Audit")->findOneById("xxxx");

.... do something somewhere to change the object ....

在这一点上,如果我打电话

$audit = $dm->getRepository("WGenSimschoolsBundle:Audit")->findOneById("xxxx");

审计数据没有改变。它仍然具有最初获取的对象。如果我尝试

$dm->refresh($audit) 

我也遇到了同样的问题。有没有办法让我返回数据库获取这个值?


我认为Doctrine默认不使用缓存。你是否覆盖了findOneById方法? - fkoessler
我在想php-mongo驱动程序是否会缓存结果... 我在Doctrine文档中也找不到任何关于缓存的说明。 - daSn0wie
7个回答

63

你已经将更改刷新到$audit对象了吗?

$audit = $dm->getRepository("WGenSimschoolsBundle:Audit")->findOneById("xxxx");
//do something somewhere to change the object
$dm->flush();

每次执行findBy(...)findOneBy(...)时,实际上都会从数据库中提取新的文档。(您应该在Symfony分析器中看到查询)

相反,使用find(),它将从其内部代理缓存中提取文档。 文档保留在代理缓存中,直到调用$dm->clear()方法。


26
根据我的经验(见我的答案),findOneBy 也使用了缓存!我不得不使用 $em->clear(); - achedeuzot
Doctrine ORM 有一个“查询缓存”,ODM 实际上并没有,你确定你的结果不是来自那个缓存吗? - Madarco
4
清除缓存将删除所有保存在其中的实体,因此,如果用户被持久化(例如),Doctrine 将会抛出一个错误,因为它不知道这个已存在的用户,并且提议应用“级联持久化”来添加它。但是,用户已经存在,因此该解决方案在这种情况下并不有效。在我看来,最好的解决方案是使用 $entityManager->refresh($entity); 强制从数据库中直接重新加载实体。 - Nicolas Facciolo

46

这对我起作用了:

$doc = $this->documentManager->getRepository('MyBundle:MyDoc')->find($id);

/* ... in the meanwhile another external process is doing some changes to the object ...*/
$doc = $this->documentManager->getRepository('MyBundle:MyDoc')->find($id); // Perhaps this is not useful
$this->documentManager->refresh($doc);

3
感谢您指出刷新选项 - 我已经寻找它很久了。 - BispensGipsGebis
是的...你的答案对我也起作用了,比那个得票更多的答案还要好。 - chuckedw
这应该是被接受的。 - user2342558

29

在之前的回答基础上,我正在寻找如何刷新一个Entity的数据库,而不是Document的数据库,但解决方案很接近。我在这里发布它,为其他遇到相同问题的人提供帮助。

在我的功能测试中,我使用了两次相同的查询:

$em = $kernel->getContainer()->get('doctrine.orm.entity_manager');
$user = $em->getRepository('AcmeUserBundle:User')->findOneBy(array('email' => 'james.bond@secure.gov.co.uk'));
echo "Old hash: ".$user->getPassword() . "\n";
// result: 8bb6118f8fd6935ad0876a3be34a717d32708ffd

然后测试经过更改密码的流程。我随后再次查询用户,以比较密码哈希是否与相同的查询发生了更改:

$user = $em->getRepository('AcmeUserBundle:User')->findOneBy(array('email' => 'james.bond@secure.gov.co.uk'));
echo "New hash: ".$user->getPassword() . "\n";
// result: 8bb6118f8fd6935ad0876a3be34a717d32708ffd # Same !

问题在于尽管经过测试的控制器更新了哈希值,但实体管理器仍将实体存储在缓存中。

因此,解决方案是在两个查询之间添加以下内容:

$em->clear();

现在,密码哈希值在查询之间发生了改变!耶!


为什么要清除整个缓存而不是仅从数据源重新加载/刷新实体对象? - MAZux
因为在写这篇文章的时候,我不知道有那个选项。也许有一种更细粒度的方式可以刷新/清空缓存。 - achedeuzot

9
您可以使用refresh方法:
$post; # modified
$entityManager->refresh();
$post; # reset from db

1
这只是偶然起作用的。refresh 从数据源重新加载单个实体,你在示例中省略了一个必需的参数。你应该调用 $em->clear() 来分离所有实体。 - Niels Keurentjes

3

当处理相关实体时,如果您通过父对象修改相关实体并保存它们,您将需要添加一个选项cascade={"detach"}才能使分离生效。

例如,假设您想通过向列表中添加新对象、删除其中一些对象并更新某些现有对象来更新Person对象上的Friends列表。您需要

$em->flush();
$em->detach($entity);

在你的Person实体中,确保更新你的friends关系:

@ORM\OneToMany(targetEntity="Somewhere\PeopleBundle\Entity\Person", mappedBy="person", cascade={"detach"})
private $friends;

这是预期的行为吗? - user2268997
$em->detach($entity); 已经过时。请使用 $em->refresh($entity);。 - Mutatos

1
尝试像这样做一些事情:
$dm->getUnitOfWork()->clear('WGenSimschoolsBundle:Audit');

这对我没有起作用。我仍然得到相同的对象返回。 - daSn0wie

0

你需要使用 EntityManager#clear()。请参见文档


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接