Doctrine在foreach循环后只插入一条记录

3

我有一个简单的数组,其中包含需要插入到数据库中的某些值。在所有的值中,只有最后一个被插入。

当我把 flush(); 放在循环内时,记录确实被插入了。

当我在 flush 之前(循环外部)dump 实体管理器时,我确实看到了对所有值(实体)的引用。

尽管如此,只有最后一条记录被插入。它得到了 id #3,所以其他的记录可能会丢失。

    $values = [
        "val1", "val2", "val3"
    ];

    foreach ($values as $value) {
      $i = new MyEntityClass();
      $i->setVerified(false);
      $i->setName($value);
      $this->em->persist($i);
    }
    $this->em->flush();
更新: 我为"pre"和"post"刷新事件添加了监听器。
  public function preFlush(PreFlushEventArgs $args) {
    $em = $args->getEntityManager();

    foreach ($em->getUnitOfWork()->getScheduledEntityInsertions() as $entity) {
      dump($entity->getName());
    }
  }

  public function postFlush(PostFlushEventArgs $args) {
    dd($args->getEntityManager()->getUnitOfWork()->getScheduledEntityInsertions());
  }
在 preFlush 阶段,所有值都被清晰地打印出来,而 postFlush dump 为空。 更新2: 我正在使用 uuid_binary_ordered_time 如下。
  /**
   * @ORM\Id
   * @ORM\Column(type="uuid_binary_ordered_time", unique=true)
   * @GeneratedValue(strategy="CUSTOM")
   * @CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidOrderedTimeGenerator")
   * @Groups({"uuid"})
   */
  protected $id;

使用 10.4.8-MariaDBphp v7.3.10

更新 3: 我仍在努力解决问题,现在有了不同的情况,但仍然遇到多个操作未执行的问题。这可能是与数据库配置相关而非Doctrine本身的问题吗?

场景:从表A和表B收集数据,将数据插入表C,从表A中删除记录。

根据@Jakumi的建议(或查看日志),我可以看到执行了以下查询(剧透:表A中的行已被删除,但表C中没有新行):

"queries": {
  "1": {
    "sql": "SELECT t0.email AS email_1, t0.status AS status_2, t0.payment_provider_id AS payment_provider_id_3, t0.payment_method AS payment_method_4, t0.quantity AS quantity_5, t0.price_total AS price_total_6, t0.created_on AS created_on_7, t0.updated_on AS updated_on_8, t0.paid_on AS paid_on_9, t0.id AS id_10, t0.s_uuid AS s_uuid_11, t0.product_id AS product_id_12 FROM payment_pending t0 WHERE t0.payment_provider_id = ? LIMIT 1",
    "params": [
      "tr_S432rV6fhM"
    ],
    "types": [
      "string"
    ],
    "executionMS": 0.0005559921264648438
  },
  "2": {
    "sql": "SELECT t0.username AS username_1, t0.roles AS roles_2, t0.password AS password_3, t0.email AS email_4, t0.email_verified AS email_verified_5, t0.created_on AS created_on_6, t0.registration_method AS registration_method_7, t0.has_premium AS has_premium_8, t0.premium_until AS premium_until_9, t0.verified_mobile AS verified_mobile_10, t0.active AS active_11, t0.facebook_id AS facebook_id_12, t0.google_id AS google_id_13, t0.id AS id_14, t0.s_uuid AS s_uuid_15, t16.is_a AS is_a_17, t16.wants_a AS wants_a_18, t16.firstname AS firstname_19, t16.lastname AS lastname_20, t16.screen_name AS screen_name_21, t16.function_title AS function_title_22, t16.mobile_number AS mobile_number_23, t16.birthday AS birthday_24, t16.age AS age_25, t16.zipcode AS zipcode_26, t16.city AS city_27, t16.id AS id_28, t16.s_uuid AS s_uuid_29, t16.user_id AS user_id_30 FROM user t0 LEFT JOIN user_extra t16 ON t16.user_id = t0.id WHERE t0.email = ? LIMIT 1",
    "params": [
      "some@email.com"
    ],
    "types": [
      "string"
    ],
    "executionMS": 0.0007460117340087891
  },
  "3": {
    "sql": "\"START TRANSACTION\"",
    "params": null,
    "types": null,
    "executionMS": 0.00010800361633300781
  },
  "4": {
    "sql": "INSERT INTO user_payments (status, payment_provider_id, payment_method, quantity, price_total, created_on, updated_on, paid_on, id, s_uuid, user_id, product_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
    "params": {
      "1": "open",
      "2": "tr_S432rV6fhM",
      "3": "paypal",
      "4": 1,
      "5": "17.95",
      "6": "2019-10-24T07:27:22+00:00",
      "7": null,
      "8": null,
      "9": "9dd4af76-f630-11e9-90f2-024216133c1a",
      "10": "9dd4af76-f630-11e9-90f2-024216133c1a",
      "11": "c2abb28c-f62f-11e9-b71f-024216133c1a",
      "12": "bce559e8-f5bc-11e9-85f8-024216133c1a"
    },
    "types": {
      "1": "string",
      "2": "string",
      "3": "string",
      "4": "integer",
      "5": "decimal",
      "6": "datetime",
      "7": "datetime",
      "8": "datetime",
      "9": "uuid_binary_ordered_time",
      "10": "uuid",
      "11": "uuid_binary_ordered_time",
      "12": "uuid_binary_ordered_time"
    },
    "executionMS": 0.0003437995910644531
  },
  "5": {
    "sql": "DELETE FROM payment_pending WHERE id = ?",
    "params": [
      "b836c012-f62f-11e9-80b0-024216133c1a"
    ],
    "types": [
      "uuid_binary_ordered_time"
    ],
    "executionMS": 0.0003409385681152344
  },
  "6": {
    "sql": "\"COMMIT\"",
    "params": null,
    "types": null,
    "executionMS": 0.09281802177429199
  }
},
"enabled": true,
"start": 1571902427.83485,
"currentQuery": 6

你的实体使用什么ID生成器?也许最后一个会覆盖之前的吗? - Jakumi
@Jakumi 感谢您的回复,我已经更新了问题。我正在使用uuid_binary_ordered_time。 - Svdb
这可能是一个非常奇怪的想法,但是...你是如何将第一个实体添加到数据库中的?如果是在同一个Symfony安装中完成的,您是否尝试以同样的方式添加第二个实体?因为它不应该工作,因为“克隆”本质上只是一个新实体,所以它也不应该工作。此外,您的实体是否与其他实体有任何关系?如果有,请发布它们的定义(双方)。还要仔细查看分析器并查看运行了哪些数据库查询。 - Jakumi
@Jakumi 这只是一个纯粹的命令脚本,用于将记录插入数据库。为了测试目的,我多次删除了整个数据库,创建了新的迁移脚本等。表或实体上没有设置关系。实体类是通过Symfony maker-bundle(make:entity)创建的。不过,我不太明白你所说的“不应该工作”的部分是什么意思..在每个循环中都会创建一个新的实体,并将其持久化到实体管理器中。这些应该具有不同的引用,所以应该可以工作?! - Svdb
哦,我把这个问题与另一个问题混淆了。很抱歉;o(您的情况实际上非常奇怪,很难找到入口点。也许可以通过以下方式找出实际执行的查询:https://ourcodeworld.com/articles/read/155/how-to-get-doctrine-query-stats-information-in-symfony - Jakumi
2个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
1
我仍然不确定是什么原因导致了这个问题,但我成功地“解决”了这个问题。我完全从我的开发环境中删除了MariaDB、它的组件和数据库本身。 在重新安装MariaDB后,运行mysql_secure_installation,重新创建数据库并运行迁移后,一切都按预期工作。 现在,$this->em->flush();在foreach之外插入所有记录,同样适用于我的第二个问题案例。

这里发生了同样的事情。我觉得我们不应该去hack本应该正常工作的东西。尝试重新构建我的Docker环境,但似乎并没有帮助。 - Sharkfin
算了,我的循环结构错了!!现在好了。 - Sharkfin

1
为什么你不像这样在persist之后立即使用flush:
$values = [
    "val1", "val2", "val3"
];

foreach ($values as $value) {
  $i = new MyEntityClass();
  $i->setVerified(false);
  $i->setName($value);
  $this->em->persist($i);
  $this->em->flush();
}
或者更好的方法是:
public function save(MyEntityClass $myEntityClass): void
{
    $this->em->persist($myEntityClass);
    $this->em->flush();
}
在实体存储库中保存相关实体,并在需要单独保存相关实体时使用它。 编辑-另一种尝试:
$values = [
    "val1", "val2", "val3"
];

$this->em->getConnection()->beginTransaction();

foreach ($values as $value) {
  $i = new MyEntityClass();
  $i->setVerified(false);
  $i->setName($value);
  $this->em->persist($i);
}

try {
    $this->em->flush();
    $this->em->getConnection()->commit();
} catch (Exception $e) {
    $this->em->getConnection()->rollback();
    $this->em->close();
    throw $e;
}
还要在$id的定义中添加一行:
/**
* @var \Ramsey\Uuid\UuidInterface
*
* @ORM\Id
* @ORM\Column(type="uuid_binary_ordered_time", unique=true)
* @GeneratedValue(strategy="CUSTOM")
* @CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidOrderedTimeGenerator")
* @Groups({"uuid"})
*/
protected $id;

编辑 - 使用type="uuid"

更新$id的声明

/**
 * Entity constructor.
 *
 * @param UuidInterface|null $uuid
 *
 * @throws \Exception
 */
public function __construct(UuidInterface $uuid = null)
{
    if (null === $uuid) {
        $uuid = Uuid::uuid4();
    }
    $this->id = $uuid;

    [Other fields ...]
}

 /**
 * @var \Ramsey\Uuid\UuidInterface
 *
 * @ORM\Id
 * @ORM\Column(type="uuid")
 */
protected $id;

/**
 * @return UuidInterface|null
 */
public function getId(): ?UuidInterface
{
    return $this->id;
}

在这样做时,您应该排除掉 $id 与 Doctrine 冲突的可能性...我们可以在其他地方进行调查。


1
因为在N条记录的情况下,这将导致N个插入查询。当在N次持久化后刷新时,应该为所有已持久化的实体执行1个插入查询。 - Svdb
1
@Svdb 这很大程度上取决于实体的设置以及它可能具有的各种关系,肯定在交易中存在某些不规则的地方。我已经更新了上面的代码,请尝试。 - Nobady
是的,我的意思是要添加@ var,但目前我认为这并不能解决问题...你有任何实体订阅吗? - Nobady
1
好的,你可以进行另一个测试。(否则我们等待有人给我们帮助:-)) 在上面的代码中使用"type" uuid"而不是"uuid_binary_ordered_time"。请更新代码。 - Nobady
1
我必须承认,你在找到不同的方法来做这件事方面相当有创意 :) 不过不幸的是,我必须报告它并没有改变结果。 - Svdb
显示剩余3条评论

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