Doctrine - 代理实体未正确更新
Doctrine - Proxy entity is not properly updated
场景
- 我有一个实体
Device
,它与 Attribute
实体 具有双向 OneToMany 关系
- 我有一个 CLI 进程,它监听传入的请求并最终做一些事情发回响应
- 我有一个 Behat 测试,它检查设备属性是否已正确更改,向所述侦听器发送请求
行为测试
事情是这样的:
- 我通过 Doctrine 存储库从数据库中获得了一个
Device
对象 - 该设备有一个 Attribute
集合尚未初始化(延迟加载)
- 因为我需要知道一个特定属性的 属性,这个特定属性从 Doctrine 加载并因此被初始化 - 它仍然是一个
Proxy
但 __isInitialized__
设置为 true
- 我向侦听器发送请求,要求更改此特定设备属性
- 监听器接收到请求并根据请求更改属性值(例如
attribute->setValue(true)
)
- 侦听器发回确认更改的响应
- 现在测试通过
Attribute
实体存储库获取给定设备的属性,以检查它的值是否已从侦听器 在数据库中正确更改
问题出在第6点:属性值错误.
现在有人可能认为这是正常的,因为对象已经加载并在 Behat 测试中标记为已初始化,所以 Doctrine 不知道并发进程所做的更改,这很好。
然而,真正奇怪的是,Doctrine 无论如何都在第 6 点执行查询(使用 Doctrine SQL 日志检查)并且该查询返回的结果集包含该设备属性的不同值(即.不同value
属性)但是属性变量不会自动更新!
事实上,为了更新它,我必须在属性上调用 $em->refresh()
或在第 6 点获取属性之前使用 $em->clear()
清除缓存。
这是 Doctrine 中的错误还是什么?有什么想法吗?
我想我找到了我要找的答案。
通过 Doctrine 类 进行调试(特别是在 UnitOfWork::createEntity()
中)我发现 Doctrine 不会覆盖现有实体数据,除非查询明确指定 Query::HINT_REFRESH
提示:
$result = $this->getEntityManager()->createQueryBuilder()
->select('e')
->from(MyEntity::class, 'e')
->getQuery()->setHint(Query::HINT_REFRESH, true)->getResult();
这样,即使实体已经加载到内存中,如果有新的数据库数据可用于该实体,Doctrine 将覆盖实体属性。此外,这不会像 $em->refresh()
那样触发新查询。
其实这也被official documentation证实了:
Query::HINT_REFRESH - This query is used internally by
EntityManager::refresh() and can be used in userland as well. If you
specify this hint and a query returns the data for an entity that is
already managed by the UnitOfWork, the fields of the existing entity
will be refreshed. In normal operation a result-set that loads data of
an already existing entity is discarded in favor of the already
existing entity.
场景
- 我有一个实体
Device
,它与Attribute
实体 具有双向 OneToMany 关系
- 我有一个 CLI 进程,它监听传入的请求并最终做一些事情发回响应
- 我有一个 Behat 测试,它检查设备属性是否已正确更改,向所述侦听器发送请求
行为测试
事情是这样的:
- 我通过 Doctrine 存储库从数据库中获得了一个
Device
对象 - 该设备有一个Attribute
集合尚未初始化(延迟加载) - 因为我需要知道一个特定属性的 属性,这个特定属性从 Doctrine 加载并因此被初始化 - 它仍然是一个
Proxy
但__isInitialized__
设置为true
- 我向侦听器发送请求,要求更改此特定设备属性
- 监听器接收到请求并根据请求更改属性值(例如
attribute->setValue(true)
) - 侦听器发回确认更改的响应
- 现在测试通过
Attribute
实体存储库获取给定设备的属性,以检查它的值是否已从侦听器 在数据库中正确更改
问题出在第6点:属性值错误.
现在有人可能认为这是正常的,因为对象已经加载并在 Behat 测试中标记为已初始化,所以 Doctrine 不知道并发进程所做的更改,这很好。
然而,真正奇怪的是,Doctrine 无论如何都在第 6 点执行查询(使用 Doctrine SQL 日志检查)并且该查询返回的结果集包含该设备属性的不同值(即.不同value
属性)但是属性变量不会自动更新!
事实上,为了更新它,我必须在属性上调用 $em->refresh()
或在第 6 点获取属性之前使用 $em->clear()
清除缓存。
这是 Doctrine 中的错误还是什么?有什么想法吗?
我想我找到了我要找的答案。
通过 Doctrine 类 进行调试(特别是在 UnitOfWork::createEntity()
中)我发现 Doctrine 不会覆盖现有实体数据,除非查询明确指定 Query::HINT_REFRESH
提示:
$result = $this->getEntityManager()->createQueryBuilder()
->select('e')
->from(MyEntity::class, 'e')
->getQuery()->setHint(Query::HINT_REFRESH, true)->getResult();
这样,即使实体已经加载到内存中,如果有新的数据库数据可用于该实体,Doctrine 将覆盖实体属性。此外,这不会像 $em->refresh()
那样触发新查询。
其实这也被official documentation证实了:
Query::HINT_REFRESH - This query is used internally by EntityManager::refresh() and can be used in userland as well. If you specify this hint and a query returns the data for an entity that is already managed by the UnitOfWork, the fields of the existing entity will be refreshed. In normal operation a result-set that loads data of an already existing entity is discarded in favor of the already existing entity.