How do I force doctrine to reload the data from the database?
Asked Answered
C

8

54

I'm using doctrine/mongodb 1.0.0-BETA1 in a symfony2.1 install.

So i'm trying to force my repository to call data from my database instead of using the object it has cached.

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

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

At this point if I call

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

The audit data hasn't changed. It still has the object it originally fetched. If I try to

$dm->refresh($audit) 

I get the same thing. Is there anyway for me to go back to the database for the value?

Charmain answered 7/2, 2013 at 22:0 Comment(2)
I think doctrine doesn't use caching by default.. Have you overriden the findOneById method?Eruption
i'm wondering if the php-mongo driver caches the results... i couldn't find anything in the doctrine documentation that talked about caching eitherCharmain
Z
64

Have you flushed your changes to the $audit object?

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

Every time you do a findBy(...) or findOneBy(...) it actually fetches a new document from the DB. (you should see the query in the Symfony profiler)

With a find() instead, it will fetch the document from his internal proxy cache. The Documents stay in the proxy cache until you call the $dm->clear() method.

Zitella answered 29/4, 2013 at 9:34 Comment(3)
In my case (see my answer), the findOneBy did use cache too ! I had to use $em->clear();Begga
Doctrine ORM has a "Query cache", that ODM doesn't (actually) have, are you sure your result didn't came from that cache?Zitella
Clear remove all entities saved in the cache, so if a the user is persisted (for exemple), doctrine will throw an error because it does not know this existing user and doctrine propose to apply a "Cascade persist" in order to add it. But the user already exist. So this solution is not effective in that case. According to me, the best solution is to use $entityManager->refresh($entity); in order to force reload the entity directly from the database.Eviaevict
S
48

This worked for me:

$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);
Slug answered 28/3, 2013 at 17:24 Comment(3)
Thanks for pointing to the refresh option - been looking for that for agesDartmouth
Yes... you're answer worked for me too, instead of that with more votes.Vilberg
This should be the accepted one.Frymire
B
31

Adding to the previous answers, I was searching how to refresh the database of an Entity, not of a Document but the solution was close. I'm posting it here for others stumbling on this page with the same problem.

In one of my functional tests, I was using two times the same query:

$em = $kernel->getContainer()->get('doctrine.orm.entity_manager');
$user = $em->getRepository('AcmeUserBundle:User')->findOneBy(array('email' => '[email protected]'));
echo "Old hash: ".$user->getPassword() . "\n";
// result: 8bb6118f8fd6935ad0876a3be34a717d32708ffd

Then the tests goes through a process of changing the password. I then re-queried the user to compare if the password hash had changed with the same query:

$user = $em->getRepository('AcmeUserBundle:User')->findOneBy(array('email' => '[email protected]'));
echo "New hash: ".$user->getPassword() . "\n";
// result: 8bb6118f8fd6935ad0876a3be34a717d32708ffd # Same !

The problem was that even though the tested controller updated the hash, the entity manager had the entity in cache.

So, the solution was to add the following between the two queries:

$em->clear();

And now, the password hash changed between queries ! Yay !

Begga answered 17/4, 2014 at 10:4 Comment(2)
Why to clear the whole cache instead of just reloading/refresh the entity object from data source?Proserpina
Because at the time of writing this, I didn't know there was that option. Maybe there's a more granular way the flush/refresh.Begga
A
10

You can use method refresh :

$post; # modified
$entityManager->refresh();
$post; # reset from db
Alienee answered 19/3, 2017 at 21:58 Comment(1)
This works by accident only. refresh reloads a single entity from the data source, you're omitting a required parameter in your example. You should call $em->clear() to detach all entities instead.Dekeles
F
3

When you deal with related entities, if you modify the related entities and save them through the parent object, you will have to add an option cascade={"detach"} for the detach to be effective.

For example, let's say you want to update a list of Friends on a Person object, by adding new objects to the list, removing some of them and updating some existing ones. You're going to have

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

And in your Person entity make sure to update your friends relation :

@ORM\OneToMany(targetEntity="Somewhere\PeopleBundle\Entity\Person", mappedBy="person", cascade={"detach"})
private $friends;
Fleur answered 31/3, 2014 at 9:53 Comment(2)
Is this expected Behaviour?Airdrop
$em->detach($entity); is depricated. Use $em->refresh($entity);Logjam
A
1

Try something like

$dm->getUnitOfWork()->clear('WGenSimschoolsBundle:Audit');
Aleurone answered 10/2, 2013 at 13:37 Comment(1)
this didn't work for me. I'm still getting the same object backCharmain
O
0

Large datasets may require additional considerations:

  • \Doctrine\ORM\EntityManager::refresh() only accepts one object at a time, so you can flood DB with consecutive identical SELECT ... WHERE id = ? queries.
  • \Doctrine\ORM\EntityRepository::findBy() is not an option because hydration ignores result set altogether if the entity is already persisted.
  • \Doctrine\ORM\EntityManager::detach() may not be an option because you need to persist afterwards to avoid non-persisted entity errors, and doing so can also trigger A new entity was found through the relationship errors depending on the complexity of your entity relations.

A possible alternative is to create a repository method that composes a DQL query and enables the Query::HINT_REFRESH hint. Than hint is used during hydration (see \Doctrine\ORM\UnitOfWork::createEntity()) to discard cached data.

public function refreshByIds(array $ids): array
{
    return $this->createQueryBuilder('entity')
        ->where('entity.id in (:ids)')
        ->setParameter('ids', $ids)
        ->getQuery()
        ->setHint(Query::HINT_REFRESH, true)
        ->getResult();
}
Orlando answered 5/4 at 12:29 Comment(0)
A
-1

You need to use EntityManager#clear(). See docs.

Amortizement answered 26/12, 2022 at 9:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.