在我的PHPUnit测试方法中使用Doctrine和Symfony:
// Change username for user #1 (Sheriff Woody to Chuck Norris)
$form = $crawler->selectButton('Update')->form([
'user[username]' => 'Chuck Norris',
]);
$client->submit($form);
// Find user #1
$user = $em->getRepository(User::class)->find(1);
dump($user); // Username = "Sheriff Woody"
$user = $em->createQueryBuilder()
->from(User::class, 'user')
->andWhere('user.id = :userId')
->setParameter('userId', 1)
->select('
user
')
->getQuery()
->getOneOrNullResult()
;
dump($user); // Username = "Chuck Norris"
为什么我的两种获取用户#1的方法返回不同的结果?
答案 0 :(得分:1)
我假定* ,您之前已经在该函数中创建了要通过搜寻器编辑的User对象,并检查它是否在那里。这导致它成为一个受管实体。
数据的本质是不要与数据库神奇地自我同步,但是必须具备一定的自动性或执行某种同步方法。
find()
方法将始终尝试使用缓存(除非显式关闭,另请参见边注)。 如果您明确调用执行其他查询可能会导致缓存没有被击中,产生当前结果。 (尽管它应该更新第一个用户对象...)[已更新,由于来自Arno Hilke的评论] getResult()
(或其变种之一),则查询生成器将不会,因为您明确希望执行查询。
((((旁注:使对象保持同步是很困难的。。这主要是为了保持数据库的一致性,但是所有ACID都是必需的。数据库应假定它仅在第一次查询时就与状态一起使用,并且是数据库的唯一用户。除非必须满足其他约束并且可能发生不一致的读取,否则应提高隔离级别(另请参见:事务或更确切地说:隔离)。因此,通常不需要自动同步。Doctrine使用某些假设来提高性能(主要是:隔离/锁定是乐观的)。但是,在您的特定情况下,所有这些都是没有实际的问题...因为您实际上想要一个non-repeatable read。))))
(*,否则,您看到的行为将确实出乎意料)
一种简单的解决方案是,通过调用$em->refresh($user)
来主动和明确地同步来自数据库的数据,或者-在再次获取用户之前-调用$em->clear()
,它将分离所有实体(清除缓存,这可能会对性能产生明显影响),并允许您再次调用find
并返回正确的结果。
请注意,分离实体意味着,先前从实体管理器返回的任何对象都应丢弃并再次获取(而不是通过刷新)。
代替检查数据库,您可以对显示用户名并检查其名称已更改的页面执行不同的请求。
仅使用一个实体管理器(即:根据请求与服务器共享单元测试中的实体管理器/数据库)可能是一个合理的解决方案,但它有一系列问题。主要是省略的提交和刷新可能会避免检测。
由于服务器正在使用新的实体管理器来执行其工作,因此使用一个实体管理器来设置测试,从理论上讲-为了实际上正确地执行此操作,您应该创建另一个实体管理器来检查服务器的行为。
评论:替代解决方案1,2和3可以在最高隔离级别下使用,而最初的解决方案可能无效。