14

这里涉及三个实体:DeploymentDeploymentStepDeploymentStatusLog。我将从粘贴这些类的相关定义开始

src/My/Bundle/Entity/Deployment.php

<?php

namespace My\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\PersistentCollection;

/**
 * @ORM\Table(name="deployment")
 * @ORM\Entity()
 */
class Deployment
{
  /**
   * Status Log Entries for this deployment
   *
   * @var \Doctrine\ORM\PersistentCollection
   *
   * @ORM\OneToMany(targetEntity="DeploymentStatusLog", mappedBy="deployment", cascade={"persist","remove"})
   * @ORM\OrderBy({"created_at"="DESC"})
   */
  protected $status_logs;

  /**
   * @var \Doctrine\ORM\PersistentCollection
   *
   * @ORM\OneToMany(targetEntity="DeploymentStep", mappedBy="deployment", cascade={"persist","remove"})
   * @ORM\OrderBy({"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 My\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="deployment_step")
 * @ORM\Entity()
 */
class DeploymentStep
{
  /**
   * @var Deployment
   *
   * @ORM\ManyToOne(targetEntity="Deployment", cascade={"all"})
   * @ORM\JoinColumn(name="deployment_id", referencedColumnName="id")
   * @Gedmo\SortableGroup
   */
  private $deployment;

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

  // ...
}

src/My/Bundle/Entity/DeploymentStatusLog.php

<?php

namespace My\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="deployment_status_log")
 * @ORM\Entity()
 */
class DeploymentStatusLog
{
  /**
   * @var Deployment
   *
   * @ORM\ManyToOne(targetEntity="Deployment", cascade={"all"})
   * @ORM\JoinColumn(name="deployment_id", referencedColumnName="id", nullable=false)
   */
  protected $deployment;

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

  // ...
}

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

$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 处理时会发生什么,它会抛出一个看起来很奇怪的异常,抱怨未定义的索引:

/project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php 第 2252 行中的 /project/vendor/symfony/src/Symfony/Component/HttpKernel/Debug /ErrorHandler.php:67

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

因此,虽然我可以这样做以使应用程序的这一部分功能化,但感觉有点不对,因为这个过程应该是原子的,而且这就是事务查询的重点。

有什么线索吗?

  • Symfony 2.0.15
  • 教义 2.1.7
  • PHP 5.3.3
  • MySQL 5.1.52
  • 阿帕奇 2.2.15

编辑

请求的完整堆栈跟踪

 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): Symfony\Component\HttpKernel\Debug\ErrorHandler->handle(8, 'Undefined index...', '/mnt/hgfs/mount...', 2252, Array)
#1 /project/vendor/doctrine/lib/Doctrine/ORM/Query.php(321): Doctrine\ORM\UnitOfWork->getEntityIdentifier(Object(My\Bundle\Entity\Deployment))
#2 /project/vendor/doctrine/lib/Doctrine/ORM/Query.php(274): Doctrine\ORM\Query->processParameterValue(Object(My\Bundle\Entity\Deployment))
#3 /project/vendor/doctrine/lib/Doctrine/ORM/Query.php(243): Doctrine\ORM\Query->processParameterMappings(Array)
#4 /project/vendor/doctrine/lib/Doctrine/ORM/AbstractQuery.php(607): Doctrine\ORM\Query->_doExecute()
#5 /project/vendor/doctrine/lib/Doctrine/ORM/AbstractQuery.php(413): Doctrine\ORM\AbstractQuery->execute(Array, 1)
#6 /project/vendor/gedmo-doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php(344): Doctrine\ORM\AbstractQuery->getResult()
#7 /project/vendor/gedmo-doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php(133): Gedmo\Sortable\SortableListener->getMaxPosition(Object(Doctrine\ORM\EntityManager), Object(Doctrine\ORM\Mapping\ClassMetadata), Array, Object(My\Bundle\Entity\DeploymentStep))
#8 /project/vendor/gedmo-doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php(100): Gedmo\Sortable\SortableListener->processInsert(Object(Doctrine\ORM\EntityManager), Array, Object(Doctrine\ORM\Mapping\ClassMetadata), Object(My\Bundle\Entity\DeploymentStep))
#9 /project/vendor/doctrine-common/lib/Doctrine/Common/EventManager.php(64): Gedmo\Sortable\SortableListener->onFlush(Object(Doctrine\ORM\Event\OnFlushEventArgs))
#10 /project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php(280): Doctrine\Common\EventManager->dispatchEvent('onFlush', Object(Doctrine\ORM\Event\OnFlushEventArgs))
#11 /project/vendor/doctrine/lib/Doctrine/ORM/EntityManager.php(334): Doctrine\ORM\UnitOfWork->commit()
#12 /project/src/My/Bundle/Controller/DeploymentController.php(214): Doctrine\ORM\EntityManager->flush()
#13 [internal function]: My\Bundle\Controller\DeploymentController->createAction(Object(My\Bundle\Entity\Release), Object(Symfony\Component\HttpFoundation\Request))
#14 /project/vendor/bundles/JMS/SecurityExtraBundle/Security/Authorization/Interception/MethodSecurityInterceptor.php(73): ReflectionMethod->invokeArgs(Object(My\Bundle\Controller\DeploymentController), Array)
#15 /project/app/cache/dev/classes.php(9391) : eval()'d code(1): JMS\SecurityExtraBundle\Security\Authorization\Interception\MethodSecurityInterceptor->invoke(Object(JMS\SecurityExtraBundle\Security\Authorization\Interception\MethodInvocation), Array)
#16 [internal function]: {closure}(Object(My\Bundle\Entity\Release), Object(Symfony\Component\HttpFoundation\Request))
#17 /project/app/cache/dev/classes.php(3925): call_user_func_array(Object(Closure), Array)
#18 /project/app/cache/dev/classes.php(3895): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#19 /project/app/cache/dev/classes.php(4899): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#20 /project/app/bootstrap.php.cache(551): Symfony\Bundle\FrameworkBundle\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#21 /project/web/app_dev.php(18): Symfony\Component\HttpKernel\Kernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#22 {main}

编辑 2

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

/**
 * @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()
  );
}
4

2 回答 2

9

这不是一个“解决方案”,但我希望它为其他人提供更多故障排除信息。

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

教义推荐的方法

$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 次插入后失败并出现相同的错误。

更改持久化顺序

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

删除清除

如果我只是$em->clear()在 $batchSize 检查中注释掉并仅在循环完成后才明确,那么它就会超时:

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

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

禁用事件监听器

另一个答案提到事件侦听器可能会导致此问题,因此我按照建议禁用了它们:

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

但这也不起作用......虽然看起来这可能是问题,不知何故,这实际上并没有成功地禁用它们。

验证架构

我还验证了我的架构:

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.
于 2014-11-19T23:46:49.883 回答
9

我遇到了同样的问题,就我而言,这是因为我在生命周期事件中做一个实体remove。根据我在 UnitOfWork 逻辑中的理解,您不能在该事件中调用 flush 。这样做将刷新任何从第一次调用刷新时仍待刷新的操作,这些动作已经被事件中的刷新调用删除。在弄清楚这一点后,我找到了 Doctrine 手册的这一部分,证实了我的怀疑:flushpostRemove

http://docs.doctrine-project.org/en/latest/reference/events.html#postupdate-postremove-postpersist

您认为单独刷新每个步骤是一种不好的方法是正确的,但是如果您不使用 LifeCycle 事件,那么我不确定是什么导致了您的特定问题。我能够通过$oidexecuteDeletions. UnitOfWork.php我注意到同一个 id 被反复删除,一旦从 取消设置$this->entityIdentifiers,后续删除将失败。

我的解决方案是简单地对事件中的每个 ID 进行编目postRemove,然后实际删除postFlush事件中的实体,这不是生命周期事件,因此可以进行后续持久化操作:

http://docs.doctrine-project.org/en/latest/reference/events.html#lifecycle-events

我相信你已经继续前进了,但万一其他人遇到这个问题......

于 2013-05-22T20:50:47.473 回答