【发布时间】:2014-01-18 15:16:00
【问题描述】:
我猜这是一个 Doctrine 错误(我已经在 JIRA 问题跟踪器上提交了一个问题),但万一这只是用户错误,我决定在这里发布。
概要
将实体集合保存在具有复合主节点的连接表中 由 2 个外键和一个元数据字段组成的键在某些情况下会失败。代码基于此处的说明:Doctrine docs
问题详情
-
成功:当 FOREIGN KEY 1 在要持久化的集合中的项目之间相同时, 并且 FOREIGN KEY 2 大于任何现有 PRIMARY KEY 中的 FOREIGN KEY 2,实体 和集合中的相关实体正确持久化:
示例:GPA“add val below”存在并具有评估值
{"assessment":6,"value":4}我们将尝试添加一个新的评估值,其中评估 ID > 任何现有的评估值 GPA 评估值“在下方添加 val”请求负载:
{"name":"add val below","courses":[],"assessmentValues":[{"assessment":6,"value":4},{"assessment":7,"value":3}]}-
调试日志:
[2013-12-31 11:48:48] app.INFO: GPA ID PRESAVE 在控制器中:9 [] [] [2013-12-31 11:48:48] app.INFO:保存在控制器评估 VAL 评估 ID:7 [] [] [2013-12-31 11:48:48] app.INFO: PRESAVE 在控制器评估 VAL POINTS:3 [] [] [2013-12-31 11:48:48] app.INFO: GPA ID PRESAVE 在控制器中:9 [] [] [2013-12-31 11:48:48] app.INFO:保存在控制器评估 VAL 评估 ID:6 [] [] [2013-12-31 11:48:48] app.INFO: PRESAVE 在控制器评估 VAL POINTS:4 [] [] [2013-12-31 11:48:48] 教义.调试:“开始交易”[][] [2013-12-31 11:48:48]教义.DEBUG:插入gpa_assessment_value(point_value,grade_point_average_id,assessment_id)值(?,?,?){“1”:3,“2”:“9”, 3":"7"} [] [2013-12-31 11:48:48] 教义。调试:更新 gpa_assessment_value SET point_value = ?在哪里grade_point_average_id =? AND 评估 ID = ? [4,9,6] [] [2013-12-31 11:48:48] 学说.DEBUG: "COMMIT" [] []
-
失败:当集合中的项之间的 FOREIGN KEY 1 相同,并且 FOREIGN KEY 2 小于任何现有的 FOREIGN KEY 2 时,工作单元会尝试插入现有实体并且不会对新实体进行操作。
示例:GPA“add val above”存在并具有评估值
{"assessment":8,"value":2}我们将尝试添加一个新的评估值,其中assessment_id请求负载:
{"name":"add val above","courses":[],"assessmentValues":[{"assessment":6,"value":4},{"assessment":8,"value":2}]}-
调试日志:
[2013-12-31 11:53:59] app.INFO: GPA ID PRESAVE IN CONTROLLER:10 [] [] [2013-12-31 11:53:59] app.INFO:保存在控制器评估 VAL 评估 ID:8 [] [] [2013-12-31 11:53:59] app.INFO: PRESAVE 在控制器评估 VAL POINTS:2 [] [] [2013-12-31 11:53:59] app.INFO: GPA ID PRESAVE IN CONTROLLER:10 [] [] [2013-12-31 11:53:59] app.INFO:保存在控制器评估 VAL 评估 ID:6 [] [] [2013-12-31 11:53:59] app.INFO: PRESAVE 在控制器评估 VAL POINTS:4 [] [] [2013-12-31 11:53:59] 学说。调试:“开始交易”[][] [2013-12-31 11:53:59] 教义.调试:插入 gpa_assessment_value(point_value,grade_point_average_id,assessment_id)值(?,?,?){“1”:2,“2”:“10”, 3":"8"} [] [2013-12-31 11:53:59] 教义.调试:“回滚”[][] [2013-12-31 11:53:59] request.CRITICAL:未捕获的 PHP 异常 Doctrine\DBAL\DBALException:“执行'INSERT INTO gpa_assessment_value (point_value,grade_point_average_id,assessment_id) VALUES (?, ?, ?) ' 带参数 [2, "10", "8"]: SQLSTATE [23505]:唯一违规:7 错误:重复键值违反唯一约束“gpa_assessment_value_pkey”
代码
迁移.sql
创建表评估 ( id bigserial NOT NULL, scale_id bigint 非空, 标题 varchar NOT NULL, 传递布尔值 NOT NULL, 排名int, 主键(id) ); 创建表评估_规模 ( id bigserial NOT NULL, 名称 varchar NOT NULL, 主键(id) ); -- ... 创建表grade_point_average ( id bigserial NOT NULL, 名称 varchar NOT NULL, additional_credit_allowance numeric(4, 2), 主键(id) ); -- ... 创建表 gpa_assessment_value ( grade_point_average_id bigint 非空, 评估id bigint 非空, point_value numeric(4, 2) NOT NULL, 主键(assessment_id,grade_point_average_id), FOREIGN KEY (assesment_id) REFERENCES 评估, 外键 (grade_point_average_id) 参考 Grade_point_average );模型/GradePointAverage.php
命名空间 MyApp\Model; 使用 Doctrine\ORM\Mapping\Entity; 使用 Doctrine\ORM\Mapping\Id; 使用 Doctrine\ORM\Mapping\GeneratedValue; 使用 Doctrine\ORM\Mapping\Column; //... 使用 Doctrine\Common\Collections\Collection; 使用 Doctrine\Common\Collections\ArrayCollection; 使用 MyApp\Util\ConstructorArgs; 使用 MyApp\Model\GradePointAverage\AssessmentValue; // ... /** * @Entity("MyApp\Repository\GradePointAverageRepository") */ 班级 GradePointAverage { 使用 ConstructorArgs; /** * @ID * @GeneratedValue * @Column(type="bigint") * * @var 整数 */ 私人 $id; // ... /** * @OneToMany(targetEntity="MyApp\Model\GradePointAverage\AssessmentValue", mappedBy="gradePointAverage", cascade="persist") * * @var 集合 */ 私人 $assesmentValues; // ... /** * @param 数组 $args */ 公共函数 __construct(数组 $args = []) { $this->assessmentValues = new ArrayCollection; // ... $this->handleArgs($args); } // ... /** * @return 集合 */ 公共函数 getAssessmentValues() { 返回 $this->assessmentValues; } /** * @param ArrayCollection $assessmentValues */ 公共函数 setAssessmentValues(ArrayCollection $assessmentValues) { $this->assessmentValues = $assessmentValues; } /** * @param AssessmentValue $assessmentValue */ 公共函数 addAssessmentValue(AssessmentValue $assessmentValue) { $this->assessmentValues->add($assessmentValue); } /** * @param AssessmentValue $assessmentValue */ 公共函数 removeAssessmentValue(AssessmentValue $assessmentValue) { $this->assessmentValues->removeElement($assessmentValue); } // ... }模型/GradePointAverage/AssessmentValue.php
命名空间 MyApp\Model\GradePointAverage; 使用 Doctrine\ORM\Mapping\Entity; 使用 Doctrine\ORM\Mapping\Table; 使用 Doctrine\ORM\Mapping\Column; 使用 Doctrine\ORM\Mapping\Id; 使用 Doctrine\ORM\Mapping\GeneratedValue; 使用 Doctrine\ORM\Mapping\ManyToOne; 使用 Doctrine\ORM\Mapping\JoinColumn; 使用 MyApp\Model\GradePointAverage; 使用 MyApp\Model\Assessment; 使用 MyApp\Util\ConstructorArgs; /** * @Entity("MyApp\Repository\GradePointAverage\AssessmentValueRepository") * @Table("gpa_assessment_value") */ 类评估值 { 使用 ConstructorArgs; /** * @ID * @ManyToOne(targetEntity="MyApp\Model\GradePointAverage") */ 私人 $gradePointAverage; /** * @ID * @ManyToOne(targetEntity="MyApp\Model\Assessment") */ 私人$评估; /** * @Column("point_value") * * @var 浮动 */ 私人价值; /** * @param 数组 $args */ 公共函数 __construct(数组 $args = []) { $this->handleArgs($args); } /** * @return GradePointAverage */ 公共函数 getGradePointAverage() { 返回 $this->gradePointAverage; } /** * @param GradePointAverage $gradePointAverage */ 公共函数 setGradePointAverage(GradePointAverage $gradePointAverage) { $this->gradePointAverage = $gradePointAverage; } /** * @return 评估 */ 公共函数 getAssessment() { 返回 $this-> 评估; } /** * @param 评估 $assesment */ 公共函数 setAssessment(Assessment $assessment) { $this->评估 = $评估; } /** * @return 浮点数 */ 公共函数 getValue() { 返回 $this-> 值; } /** * @param 浮动 $value */ 公共函数 setValue($value) { $this->value = $value; } /** * @return 评估量表 */ 公共函数 getAssessmentScale() { 返回 $this->assessment->getScale(); } }模型/Assessment.php
命名空间 MyApp\Model; 使用 Doctrine\ORM\Mapping\Entity; 使用 Doctrine\ORM\Mapping\Id; 使用 Doctrine\ORM\Mapping\GeneratedValue; 使用 Doctrine\ORM\Mapping\Column; 使用 Doctrine\ORM\Mapping\ManyToOne; 使用 MyApp\Model\Assessment\Scale; 使用 MyApp\Util\ConstructorArgs; /** * @Entity("MyApp\Repository\AssessmentRepository") */ 班级评估 { 使用 ConstructorArgs; /** * @ID * @GeneratedValue * @Column(type="bigint") * * @var 整数 */ 私人 $id; // ... /** * @param 数组 $args */ 公共函数 __construct(数组 $args = []) { $this->handleArgs($args); } /** * @return 整数 */ 公共函数 getId() { 返回 $this->id; } // ... }存储库/GradePointAverageRepository.php
命名空间 MyApp\Repository; 使用 Doctrine\ORM\EntityRepository; // ... 使用 MyApp\Model\GradePointAverage; 类 GradePointAverageRepository 扩展 BaseRepository 实现 GradePointAverageRepositoryInterface { // ... /** * @param GradePointAverage $gradePointAverage */ 公共函数保存(GradePointAverage $gradePointAverage) { $this->getEntityManager()->persist($gradePointAverage); $this->getEntityManager()->flush(); } }存储库/GradePointAverage/AssessmentValueRepository.php
命名空间 MyApp\Repository\GradePointAverage; 使用 Doctrine\ORM\EntityRepository; 使用 MyApp\Model\GradePointAverage\AssessmentValue; 类 AssessmentValueRepository 扩展了 EntityRepository { /** * @param AssessmentValue $assessmentValue */ 公共功能保存(评估值$评估值) { $this->getEntityManager()->persist($assessmentValue); $this->getEntityManager()->flush(); } }经理/GradePointAverageManager.php
命名空间 MyApp\Manager; 使用 InvalidArgumentException; 使用 Symfony\Component\Validator\ValidatorInterface; 使用 JMS\DiExtraBundle\Annotation\Service; 使用 JMS\DiExtraBundle\Annotation\InjectParams; 使用 JMS\SecurityExtraBundle\Annotation\PreAuthorize; 使用 Knp\Component\Pager\Pagination\PaginationInterface; 使用 MyApp\Repository\GradePointAverageRepository; 使用 MyApp\PaginationFactory\GradePointAveragePaginationFactory 接口; 使用 MyApp\Model\GradePointAverage; /** * @Service("grade_point_average_manager") */ 类 GradePointAverageManager { /** * @var GradePointAverageRepository */ 私人 $gradePointAverageRepository; /** * @var GradePointAveragePaginationFactoryInterface */ 私人 $gradePointAveragePaginationFactory; /** * @var 验证器接口 */ 私人 $validator; /** * @InjectParams * * @param GradePointAverageRepository $gradePointAverageRepository * @param GradePointAveragePaginationFactoryInterface $gradePointAveragePaginationFactory * @param ValidatorInterface $validator */ 公共函数 __construct( GradePointAverageRepository $gradePointAverageRepository, GradePointAveragePaginationFactoryInterface $gradePointAveragePaginationFactory, 验证器接口 $validator ) { $this->gradePointAverageRepository = $gradePointAverageRepository; $this->gradePointAveragePaginationFactory = $gradePointAveragePaginationFactory; $this->validator = $validator; } /** * @PreAuthorize("isAllowedToManageTheGradePointAverage(#gradePointAverage)") * @param GradePointAverage $gradePointAverage * @throws InvalidArgumentException */ 公共函数保存(GradePointAverage $gradePointAverage) { $violationList = $this->validator->validate($gradePointAverage); if ($violationList->count()) { 抛出新的 InvalidArgumentException; } $this->gradePointAverageRepository->save($gradePointAverage); } }控制器/GradePointAverageController.php
命名空间 MyApp\Controller; 使用 Symfony\Component\HttpFoundation\Request; 使用 Symfony\Component\HttpFoundation\Response; 使用 Symfony\Component\HttpKernel\Log\LoggerInterface; 使用 Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 使用 Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; 使用 Doctrine\Common\Collections\ArrayCollection; 使用 FOS\RestBundle\View\View; 使用 JMS\DiExtraBundle\Annotation\Service; 使用 JMS\DiExtraBundle\Annotation\InjectParams; 使用 JMS\SecurityExtraBundle\Annotation\PreAuthorize; 使用 Knp\Component\Pager\Pagination\PaginationInterface; 使用 MyApp\Manager\GradePointAverageManager; 使用 MyApp\Model\GradePointAverage; 使用 MyApp\Model\GradePointAverage\AssessmentValue; /** * @Service("grade_point_average_controller", parent="app.controller.abstract") * @Route("/gpa", service="grade_point_average_controller") */ 类 GradePointAverageController 扩展 BaseController { /** * @var GradePointAverageManager */ 私人 $gradePointAverageManager; 私人 $logger; /** * @InjectParams * * @param GradePointAverageManager $gradePointAverageManager * @param LoggerInterface $logger */ 公共函数 __construct(GradePointAverageManager $gradePointAverageManager, LoggerInterface $logger) { $this->gradePointAverageManager = $gradePointAverageManager; $this->logger = $logger; } // ... /** * @Route("/{id}", name="gpa.edit", requirements={"id" = "\d+"}) * @Method("PUT") * * @param 请求 $request * @param GradePointAverage $gpa * @return 查看 */ 公共函数editAction(请求$request,GradePointAverage $gpa) { $form = $this->formFactory->createNamed(null, 'gpa', $gpa, [ '方法' => 'PUT', ]); $form->handleRequest($request); foreach ($gpa->getAssessmentValues() as $av) { $this->logger->info('GPA ID 在控制器中预验证:'.$gpa->getId()); $this->logger->info('在控制器评估 VAL 评估 ID 中预验证:'.$av->getAssessment()->getId()); $this->logger->info('在控制器评估 VAL 点中预验证:'.$av->getValue()); } /* // 尝试颠倒集合的顺序,看看是否有帮助 $assessmentVals = $gpa->getAssessmentValues()->toArray(); $reversed = array_reverse($assessmentVals); $reversedColl = new ArrayCollection($reversed); $gpa->setAssessmentValues($reversedColl); */ if ($form->isValid()) { foreach ($gpa->getAssessmentValues() as $av) { $this->logger->info('GPA ID PRESAVE IN CONTROLLER:'.$gpa->getId()); $this->logger->info('PRESAVE IN CONTROLLER ASSESSMENT VAL ASSESSMENT ID:'.$av->getAssessment()->getId()); $this->logger->info('PRESAVE IN CONTROLLER ASSESSMENT VAL POINTS:'.$av->getValue()); } $this->gradePointAverageManager->save($gpa); 返回新视图($gpa,204); } 返回新视图($form); } // ... }【问题讨论】:
-
你有从实体生成的sql还是从sql生成实体?
-
我正在手动编写 SQL,然后手动编写实体。我们没有在项目中使用生成器。
-
你能不能用理论命令行生成实体并检查你和他们的映射文件(只是为了确保你在实体中写的内容与理论生成的实体是一样的。)
-
我会看一下,但我不确定这会有什么不同,因为生成器必须引用现有架构或注释中的现有实体映射。
-
因此,为了实现这一点,我从以下说明书条目的 SQL 文件生成了映射:symfony.com/doc/current/cookbook/doctrine/…。它无法识别带有元数据的连接表。它只是尝试在
GradePointAverage和Assessment之间映射ManyToMany,同时忽略点值的元数据字段。
标签: symfony orm doctrine-orm doctrine-orm-postgres