Symfony2/Doctrine2 在刷新实体管理器时抛出索引错误 [英] Symfony2 / Doctrine2 throwing index error when flushing the entity manager

查看:15
本文介绍了Symfony2/Doctrine2 在刷新实体管理器时抛出索引错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此处涉及三个实体:DeploymentDeploymentStepDeploymentStatusLog.我将首先粘贴这些类的相关定义

Three entities are involved here: Deployment, DeploymentStep, and DeploymentStatusLog. I'll start by pasting the relevant definitions of those classes

src/My/Bundle/Entity/Deployment.php

<?php

namespace MyBundleEntity;

use DoctrineORMMapping as ORM;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineORMPersistentCollection;

/**
 * @ORMTable(name="deployment")
 * @ORMEntity()
 */
class Deployment
{
  /**
   * Status Log Entries for this deployment
   *
   * @var DoctrineORMPersistentCollection
   *
   * @ORMOneToMany(targetEntity="DeploymentStatusLog", mappedBy="deployment", cascade={"persist","remove"})
   * @ORMOrderBy({"created_at"="DESC"})
   */
  protected $status_logs;

  /**
   * @var DoctrineORMPersistentCollection
   *
   * @ORMOneToMany(targetEntity="DeploymentStep", mappedBy="deployment", cascade={"persist","remove"})
   * @ORMOrderBy({"sequence" = "ASC"})
   */
  protected $steps;

  public function __construct()
  {
    $this->status_logs     = new ArrayCollection();
    $this->steps           = new ArrayCollection();
  }

  /**
   * Add status_logs
   *
   * @param DeploymentStatusLog $statusLogs
   */
  public function addDeploymentStatusLog(DeploymentStatusLog $statusLogs)
  {
      $this->status_logs[] = $statusLogs;
  }

  /**
   * Add steps
   *
   * @param DeploymentStep $steps
   */
  public function addDeploymentStep(DeploymentStep $steps)
  {
    $this->steps[] = $steps;
  }

  // ... 
}

src/My/Bundle/Entity/DeploymentStep.php

<?php

namespace MyBundleEntity;

use DoctrineORMMapping as ORM;

/**
 * @ORMTable(name="deployment_step")
 * @ORMEntity()
 */
class DeploymentStep
{
  /**
   * @var Deployment
   *
   * @ORMManyToOne(targetEntity="Deployment", cascade={"all"})
   * @ORMJoinColumn(name="deployment_id", referencedColumnName="id")
   * @GedmoSortableGroup
   */
  private $deployment;

  /**
   * Set deployment
   *
   * @param Deployment $deployment
   */
  public function setDeployment(Deployment $deployment)
  {
    $this->deployment = $deployment;
  }

  // ...
}

src/My/Bundle/Entity/DeploymentStatusLog.php

<?php

namespace MyBundleEntity;

use DoctrineORMMapping as ORM;

/**
 * @ORMTable(name="deployment_status_log")
 * @ORMEntity()
 */
class DeploymentStatusLog
{
  /**
   * @var Deployment
   *
   * @ORMManyToOne(targetEntity="Deployment", cascade={"all"})
   * @ORMJoinColumn(name="deployment_id", referencedColumnName="id", nullable=false)
   */
  protected $deployment;

  /**
   * Set deployment
   *
   * @param Deployment $deployment
   */
  public function setDeployment( Deployment $deployment)
  {
      $this->deployment = $deployment;
  }

  // ...
}

现在,当我尝试同时为这三个实体创建全新的记录时,问题就出现了.在控制器中:

Now, the problem arises when I attempt to create brand new records for all three of these entities at once. In the controller:

$em = $this->getDoctrine()->getEntityManager();

$deployment = new Deployment();

$form = $this->createForm(new DeploymentType($em), $deployment);

if ($request->getMethod() == 'POST')
{
  $form->bindRequest($request);

  if ($form->isValid())
  {
    $codeStep = new DeploymentStep();
    $codeStep->setDeployment( $deployment );
    // Other setters on DeploymentStep

    $deploymentStatusLog = new DeploymentStatusLog();
    $deploymentStatusLog->setDeployment( $deployment );
    // Other setters on DeploymentStatusLog

    $deployment->addDeploymentStep( $codeStep );
    $deployment->addDeploymentStatusLog( $deploymentStatusLog );

    $em->persist( $deployment );
    $em->flush();
  }
}

当 UnitOfWork 处理时会发生什么,它抛出一个看起来很奇怪的异常,抱怨未定义的索引:

What happens when the UnitOfWork processes, it throws a weird-looking exception complaining about an undefined index:

带有消息注意:未定义索引"的异常ErrorException":000000001294f822000000006b6f9f2c/project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php line 2252' in/project/vendor/symfony/src/Symfony/Component/HttpKernel/Debug/ErrorHandler.php:67

exception 'ErrorException' with message 'Notice: Undefined index: 000000001294f822000000006b6f9f2c in /project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php line 2252' in /project/vendor/symfony/src/Symfony/Component/HttpKernel/Debug/ErrorHandler.php:67

现在,如果我先持久化/刷新部署实体,然后然后持久化/刷新关联,它就会成功.

Now, if I persist/flush the Deployment entity first, and then persist/flush the associations, it succeeds.

所以虽然我可以这样做以使应用程序的这部分功能正常,但感觉有点错误,因为这个过程应该是原子的并且很好,这就是重点交易查询开始.

So while I can do that to make this part of the application functional, it feels kinda wrong, since this process should be atomic and well, that's the whole point of transactional queries to begin with.

有什么线索吗?

  • Symfony 2.0.15
  • 教义 2.1.7
  • PHP 5.3.3
  • MySQL 5.1.52
  • Apache 2.2.15

按请求进行完整的堆栈跟踪

Full stack trace by request

 exception 'ErrorException' with message 'Notice: Undefined index: 000000004081f5f9000000005f1dbbfc in /project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php line 2252' in /project/vendor/symfony/src/Symfony/Component/HttpKernel/Debug/ErrorHandler.php:67
Stack trace:
#0 /project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php(2252): SymfonyComponentHttpKernelDebugErrorHandler->handle(8, 'Undefined index...', '/mnt/hgfs/mount...', 2252, Array)
#1 /project/vendor/doctrine/lib/Doctrine/ORM/Query.php(321): DoctrineORMUnitOfWork->getEntityIdentifier(Object(MyBundleEntityDeployment))
#2 /project/vendor/doctrine/lib/Doctrine/ORM/Query.php(274): DoctrineORMQuery->processParameterValue(Object(MyBundleEntityDeployment))
#3 /project/vendor/doctrine/lib/Doctrine/ORM/Query.php(243): DoctrineORMQuery->processParameterMappings(Array)
#4 /project/vendor/doctrine/lib/Doctrine/ORM/AbstractQuery.php(607): DoctrineORMQuery->_doExecute()
#5 /project/vendor/doctrine/lib/Doctrine/ORM/AbstractQuery.php(413): DoctrineORMAbstractQuery->execute(Array, 1)
#6 /project/vendor/gedmo-doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php(344): DoctrineORMAbstractQuery->getResult()
#7 /project/vendor/gedmo-doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php(133): GedmoSortableSortableListener->getMaxPosition(Object(DoctrineORMEntityManager), Object(DoctrineORMMappingClassMetadata), Array, Object(MyBundleEntityDeploymentStep))
#8 /project/vendor/gedmo-doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php(100): GedmoSortableSortableListener->processInsert(Object(DoctrineORMEntityManager), Array, Object(DoctrineORMMappingClassMetadata), Object(MyBundleEntityDeploymentStep))
#9 /project/vendor/doctrine-common/lib/Doctrine/Common/EventManager.php(64): GedmoSortableSortableListener->onFlush(Object(DoctrineORMEventOnFlushEventArgs))
#10 /project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php(280): DoctrineCommonEventManager->dispatchEvent('onFlush', Object(DoctrineORMEventOnFlushEventArgs))
#11 /project/vendor/doctrine/lib/Doctrine/ORM/EntityManager.php(334): DoctrineORMUnitOfWork->commit()
#12 /project/src/My/Bundle/Controller/DeploymentController.php(214): DoctrineORMEntityManager->flush()
#13 [internal function]: MyBundleControllerDeploymentController->createAction(Object(MyBundleEntityRelease), Object(SymfonyComponentHttpFoundationRequest))
#14 /project/vendor/bundles/JMS/SecurityExtraBundle/Security/Authorization/Interception/MethodSecurityInterceptor.php(73): ReflectionMethod->invokeArgs(Object(MyBundleControllerDeploymentController), Array)
#15 /project/app/cache/dev/classes.php(9391) : eval()'d code(1): JMSSecurityExtraBundleSecurityAuthorizationInterceptionMethodSecurityInterceptor->invoke(Object(JMSSecurityExtraBundleSecurityAuthorizationInterceptionMethodInvocation), Array)
#16 [internal function]: {closure}(Object(MyBundleEntityRelease), Object(SymfonyComponentHttpFoundationRequest))
#17 /project/app/cache/dev/classes.php(3925): call_user_func_array(Object(Closure), Array)
#18 /project/app/cache/dev/classes.php(3895): SymfonyComponentHttpKernelHttpKernel->handleRaw(Object(SymfonyComponentHttpFoundationRequest), 1)
#19 /project/app/cache/dev/classes.php(4899): SymfonyComponentHttpKernelHttpKernel->handle(Object(SymfonyComponentHttpFoundationRequest), 1, true)
#20 /project/app/bootstrap.php.cache(551): SymfonyBundleFrameworkBundleHttpKernel->handle(Object(SymfonyComponentHttpFoundationRequest), 1, true)
#21 /project/web/app_dev.php(18): SymfonyComponentHttpKernelKernel->handle(Object(SymfonyComponentHttpFoundationRequest))
#22 {main}

编辑 2

创建操作的完整代码,根据要求

EDIT 2

Full code of create action, as requested

/**
 * @Route("/create/{id}", name="deployment_create_id")
 * @ParamConverter("release", class="MyBundle:Release")
 * @Method({"POST","GET"})
 * @Secure(roles="ROLE_DEPLOYMENT_PLANNER")
 * @Template()
 */
public function createAction( Release $release, Request $request )
{
  $em           = $this->getDoctrine()->getEntityManager();
  $sessionUser = $this->get('security.context')->getToken()->getUser();

  $deployment = new Deployment();
  $deployment->setRelease( $release );
  $deployment->setAuthor( $sessionUser );

  $form = $this->createForm(new DeploymentType($em), $deployment);

  if ($request->getMethod() == 'POST')
  {
    $form->bindRequest($request);

    if ($form->isValid())
    {
      $codeStep = new DeploymentStep();
      $codeStep->setDeployment( $deployment );
      $codeStep->setSequence( 0 );
      $codeStep->setTitle( "Update Code" );
      $codeStep->setDetails( "Update codebase per the plan's specifications" );
      $codeStep->setDeploymentStepType(
          $em->getRepository('MyBundle:DeploymentStepType')->findOneBy(
              array( 'name' => DeploymentStepType::TYPE_OTHER )
          )
      );

      $deploymentStatusLog = new DeploymentStatusLog();
      $deploymentStatusLog->setDeployment( $deployment );
      $deploymentStatusLog->setUser( $sessionUser );
      $deploymentStatusLog->setNotes( 'New Deployment Created' );
      $deploymentStatusLog->setDeploymentStatus(
          $em->getRepository('MyBundle:DeploymentStatus')->findOneBy(
              array( 'title' => DeploymentStatus::STATUS_NEW )
          )
      );

      $deployment->addDeploymentStep( $codeStep );
      $deployment->addDeploymentStatusLog( $deploymentStatusLog );

      try {
        $em->persist( $deployment );
           $em->persist( $codeStep );
           $em->persist( $deploymentStatusLog );
        $em->flush();

        return $this->redirectSuccess(
            'Deployment created.'
          , $release->getRouteName()
          , $release->getRouteParameters()
        );
      }
      catch ( Exception $e )
      {
        $this->setFlashErrorMessage( 'Error saving deployment.' );
      }
    }
  }

  return array(
      'release' => $release
    , 'form'    => $form->createView()
  );
}

推荐答案

这不是解决方案",但我希望它能为其他人提供更多的故障排除信息.

在遵循 Doctrine 的建议时,我遇到了同样的错误批处理批量插入.仅供参考,这是来自控制器(不是其他答案提到的生命周期事件).

I had this same exact error when following Doctrine's recommendation for batch processing mass inserts. FYI, this is from a controller (not a lifecycle event like the other answer mentions).

$batchSize = 20;
for ($i = 1; $i <= 10000; ++$i) {
    $user = new CmsUser;
    $user->setStatus('user');
    $user->setUsername('user' . $i);
    $user->setName('Mr.Smith-' . $i);
    $em->persist($user);
    if (($i % $batchSize) === 0) {
        $em->flush();
        $em->clear(); // Detaches all objects from Doctrine!
    }
}
$em->flush(); //Persist objects that did not make up an entire batch
$em->clear();

当我使用类似于上面推荐的代码时,它会在大约 2000 次插入后失败并出现相同的错误.

When I used something similar to the recommended code above, it would fail with the same errors after about 2000 inserts.

我坚持 10,000 个实体的顺序没有任何区别.如果我坚持、刷新和清除每个循环,这没有什么区别(不理想,但我尝试过).

The order I persisted the 10,000 entities made no difference. And it made no difference if I persisted, flushed and cleared every single loop (not ideal, but I tried it).

如果我只是在 $batchSize 检查中注释掉 $em->clear() 并且仅在循环完成后才将其清除,则它超时:

If I just commented out the $em->clear() within the $batchSize check and made it only clear after the loop finished, then it timed out:

Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/core/cms/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 541

所以我在脚本上放了一个 set_time_limit(3600) 以防止超时,它不再给出错误,但是内存不足:P

So I put a set_time_limit(3600) on the script to prevent time out, and it no longer gave the error, but it ran out of memory :P

这表明在循环内执行 $em->clear() 时会出现问题.这与其他问题一致.不幸的是,如果没有 $em->clear(),您会很快耗尽内存.

This would suggest that the problem occurs when $em->clear() is executed within the loop. This is consistent with other questions. Unfortunately, without $em->clear(), you run out of memory quickly.

另一个答案提到事件侦听器可能会导致这种情况,所以我按照建议禁用了它们:

The other answer mentions that Event Listeners may be causing this, so I disabled them like it suggested:

foreach ($em->getEventManager()->getListeners() as $event => $listeners) {
    foreach ($listeners as $listener) {
        $em->getEventManager()->removeEventListener($event, $listener);
    }
}

但这也不起作用......尽管这似乎可能是问题所在,不知何故,这实际上并没有成功禁用它们.

But that didn't work either... although it seems like this could be the issue, somehow, and that this doesn't actually successfully disable them.

我还验证了我的架构:

php app/console doctrine:schema:validate

并且没有报告错误.

[Mapping]  OK - The mapping files are correct.
[Database] OK - The database schema is in sync with the mapping files.

这篇关于Symfony2/Doctrine2 在刷新实体管理器时抛出索引错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆