为何这段Symfony Doctrine代码违规?苹果项AssetId始终相同
Doctrine实体重复赋值AssetId异常的原因与解决
问题本质
你遇到的问题是**Doctrine一级缓存(EntityManager缓存)**导致的:Doctrine在同一个请求周期内,会将已经从数据库加载的实体对象缓存起来,同一个数据库记录对应的实体只会存在一个实例。
代码中的问题点
- 第一次循环到
apple时,findOneBy(['name' => 'apple'])从数据库加载对应的Item实体,并存入EntityManager的缓存 - 后续再次循环到
apple时,findOneBy不会发起新的数据库查询,直接返回缓存里的同一个对象实例 - 每次循环给这个同一个实例设置新的
assetId,会覆盖之前的值,最终所有apple项的AssetId都是最后一次循环生成的随机数
违反的规则
这违反了Doctrine的实体实例唯一性规则:在同一个EntityManager上下文里,数据库中同一条记录对应的实体对象是唯一的,所有对该记录的操作都会指向同一个实例。
解决方案
根据你的需求选择对应方案:
1. 创建新的实体实例(适用于需要持久化多个同名称不同AssetId的记录)
如果你的目标是生成多个独立的Item实体(比如存入数据库),不要复用已加载的实体,而是创建新实例并复制原实体的属性:
$items = ['apple', 'banana', 'orange', 'strawberry', 'apple', 'apple', 'banana', 'orange', 'banana']; $itemRepository = $this->entityManager->getRepository(Item::class); $itemList = []; foreach($items as $itemName) { $assetId = random_int(100,10000); // 获取原实体的属性模板 $originalItem = $itemRepository->findOneBy(['name' => $itemName]); // 创建新的实体实例 $newItem = new Item(); $newItem->setName($originalItem->getName()); // 复制其他需要的属性(比如描述、分类等) $newItem->setAssetId($assetId); $itemList[] = $newItem; // 如果需要持久化到数据库,添加以下代码 // $this->entityManager->persist($newItem); } // $this->entityManager->flush(); dd($itemList);
2. 克隆实体实例(适用于临时使用不同属性,不需要持久化的场景)
如果只是需要临时持有不同AssetId的对象(不存入数据库),可以克隆已加载的实体,避免修改同一个实例:
$items = ['apple', 'banana', 'orange', 'strawberry', 'apple', 'apple', 'banana', 'orange', 'banana']; $itemRepository = $this->entityManager->getRepository(Item::class); $itemList = []; foreach($items as $itemName) { $assetId = random_int(100,10000); $item = $itemRepository->findOneBy(['name' => $itemName]); // 克隆实体生成新实例 $clonedItem = clone $item; $clonedItem->setAssetId($assetId); $itemList[] = $clonedItem; } dd($itemList);
注意:如果实体包含关联关系,克隆默认是浅克隆,需要手动处理关联对象的深克隆,或者在实体类中重写
__clone方法实现深克隆。
内容的提问来源于stack exchange,提问作者Digitaltim
相关产品推荐
相关产品推荐

