Php 使用复合主键生成自动增量ID
我想给一个实体(发票、订单、预订等)一个唯一的序列号,但仅在该年内唯一。因此,每年的第一张发票(或其他字段,如Customer)的起始id为1。这意味着可以有一个复合主键(year,id)或一个主键(即invoice_id)以及两个其他列,它们在一起是唯一的 我的问题:使用Doctrine2和Symfony2为对象提供自动生成的ID和另一个值的唯一组合的最佳方法是什么 复合密钥的原则限制 条令无法将自动生成的ID分配给具有复合主键()的实体: 具有复合密钥的每个实体都不能使用其他id生成器 而不是“分配”。这意味着ID字段必须有它们的值 在调用Php 使用复合主键生成自动增量ID,php,mysql,sql,symfony,doctrine-orm,Php,Mysql,Sql,Symfony,Doctrine Orm,我想给一个实体(发票、订单、预订等)一个唯一的序列号,但仅在该年内唯一。因此,每年的第一张发票(或其他字段,如Customer)的起始id为1。这意味着可以有一个复合主键(year,id)或一个主键(即invoice_id)以及两个其他列,它们在一起是唯一的 我的问题:使用Doctrine2和Symfony2为对象提供自动生成的ID和另一个值的唯一组合的最佳方法是什么 复合密钥的原则限制 条令无法将自动生成的ID分配给具有复合主键()的实体: 具有复合密钥的每个实体都不能使用其他id生成器 而不
EntityManager\persist($entity)
之前设置
手动设置序列号
所以我必须手动分配一个ID。为了做到这一点,我尝试寻找某一年中最高的ID,并给新实体ID+1。我怀疑这是最好的方法,即使是这样,我也没有找到最好的方法。因为我认为这是一个一般性的问题,我想避免这种情况,所以我开始问这个问题
香草选择:仅搭配MyISAM
我发现“香草”MySQL/MyISAM解决方案基于:
由于Doctrine2的局限性,这将不起作用,因此我正在寻找一种与此香草MySQL解决方案等效的doctrineORM
其他解决方案
InnoDB也有一个解决方案:与您链接到的预插入触发器解决方案相当的ORM将是一个生命周期回调。你可以阅读更多关于他们的信息 一个天真的解决方案看起来是这样的 services.yml
services:
invoice.listener:
class: MyCompany\CompanyBundle\EventListener\InvoiceListener
tags :
- { name: doctrine.event_subscriber, connection: default }
InvoiceListener.php
<?php
namespace MyCompany\CompanyBundle\EventListener;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
use MyCompany\CompanyBundle\Entity\Invoice;
class InvoiceListener implements EventSubscriber {
protected $invoices;
public function getSubscribedEvents() {
return [
'onFlush',
'postFlush'
];
}
public function onFlush(OnFlushEventArgs $event) {
$this->invoices = [];
/* @var $em \Doctrine\ORM\EntityManager */
$em = $event->getEntityManager();
/* @var $uow \Doctrine\ORM\UnitOfWork */
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() as $entity) {
if ($entity instanceof Invoice) {
$this->invoices[] = $entity;
}
}
}
public function postFlush(PostFlushEventArgs $event) {
if (!empty($this->invoices)) {
/* @var $em \Doctrine\ORM\EntityManager */
$em = $event->getEntityManager();
foreach ($this->invoices as $invoice) {
// Get all invoices already in the database for the year in question
$invoicesToDate = $em
->getRepository('MyCompanyCompanyBundle:Invoice')
->findBy(array(
'year' => $invoice->getYear()
// You could include e.g. clientID here if you wanted
// to generate a different sequence per client
);
// Add your sequence number
$invoice->setSequenceNum(count($invoicesToDate) + 1);
/* @var $invoice \MyCompany\CompanyBundle\Entity\Invoice */
$em->persist($invoice);
}
$em->flush();
}
}
}
#winces#vanilla选项的(可能)问题是在事务期间必须锁定整个表,这会导致并发的INSERT
s失效。为什么要每年重复ID?实际的id值是没有意义的;如果要输出序列,请执行自联接和COUNT()
。出于财务原因,发票必须具有序列号(每年从id=1开始)。它不必是主键的一部分,我会编辑我的问题。不,好的,是的,你可能需要一个循环,持续序列然后。。。您可能无法摆脱每年制作此系列的命运,但至少可以解决表锁定问题;将计数器存储在另一个表中(按年份键入),然后用存储过程或触发器(在它们自己的事务中)点击它。如果有人不提交他们的行,那么最终会出现间隙,但至少长时间运行的事务不会将其他人锁定在外。然后在这些列上只需要一个唯一键
。这听起来是个好主意,但我不确定我是否真的理解它。我必须如何在Symfony2/ORM中实现它?@Clockwork Muse我知道你是SQL方面的专家。我想到了一个Symfony2/Doctrine实现我找到的普通解决方案。您是否同意或正在考虑其他计划?无法使其工作,但看起来像我需要的:)。谢谢实体\发票的外观如何?顺便说一句,return[]
在这里给出了一个错误,我将其更改为return array()
发票实体的唯一添加项是其序列号的单独整数属性(由setSequenceNum设置)。这不是唯一标识符,只是一个常规的旧整数。我还假设它在这里有一个year属性(getYear),但显然,您也可以在date列或其他任何列中执行相同的操作。短数组语法实际上是PHP5.4添加的(请参阅),因此您会遇到这个错误,因为您使用的是旧版本的PHP。我仍然无法让它工作。似乎后刷新没有被调用。你能提供更多的代码示例吗?你把这个类注册为事件订阅者了吗?请使用@ORM\EntityListeners({})
查看.Yes。可以吗?
<?php
namespace MyCompany\CompanyBundle\EventListener;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
use MyCompany\CompanyBundle\Entity\Invoice;
class InvoiceListener implements EventSubscriber {
protected $invoices;
public function getSubscribedEvents() {
return [
'onFlush',
'postFlush'
];
}
public function onFlush(OnFlushEventArgs $event) {
$this->invoices = [];
/* @var $em \Doctrine\ORM\EntityManager */
$em = $event->getEntityManager();
/* @var $uow \Doctrine\ORM\UnitOfWork */
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() as $entity) {
if ($entity instanceof Invoice) {
$this->invoices[] = $entity;
}
}
}
public function postFlush(PostFlushEventArgs $event) {
if (!empty($this->invoices)) {
/* @var $em \Doctrine\ORM\EntityManager */
$em = $event->getEntityManager();
foreach ($this->invoices as $invoice) {
// Get all invoices already in the database for the year in question
$invoicesToDate = $em
->getRepository('MyCompanyCompanyBundle:Invoice')
->findBy(array(
'year' => $invoice->getYear()
// You could include e.g. clientID here if you wanted
// to generate a different sequence per client
);
// Add your sequence number
$invoice->setSequenceNum(count($invoicesToDate) + 1);
/* @var $invoice \MyCompany\CompanyBundle\Entity\Invoice */
$em->persist($invoice);
}
$em->flush();
}
}
}