Doctrine 可以处理多列 UNIQUE 索引吗?

Posted

技术标签:

【中文标题】Doctrine 可以处理多列 UNIQUE 索引吗?【英文标题】:Can Doctrine handle multiple column UNIQUE indexes? 【发布时间】:2020-02-21 13:40:08 【问题描述】:

例如,我有一个实体:

/**
 * Serie
 *
 * @ORM\Table(name="series", uniqueConstraints=@ORM\UniqueConstraint(name="unique_position", columns="chart_id", "position"))
 */
class Serie

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var int
     *
     * @ORM\Column(name="position", type="integer")
     */
    private $position;

    /**
     * @var Chart
     *
     * @ORM\ManyToOne(targetEntity="Chart", inversedBy="series")
     * @ORM\JoinColumns(
     *   @ORM\JoinColumn(name="chart_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
     * )
     */
    private $chart;

具有以下架构:

CREATE TABLE IF NOT EXISTS series (
  id INT(11) NOT NULL AUTO_INCREMENT,
  chart_id INT(11) NOT NULL,
  position INT(11) NOT NULL
  PRIMARY KEY (id),
  UNIQUE INDEX unique_position (chart_id ASC, position ASC) VISIBLE,
  UNIQUE INDEX unique_name (chart_id ASC, name ASC) VISIBLE,
  INDEX IDX_582B2D4DBEF83E0A (chart_id ASC) VISIBLE,
  CONSTRAINT FK_582B2D4DBEF83E0A
    FOREIGN KEY (chart_id)
    REFERENCES chart (id)
    ON DELETE CASCADE)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_unicode_ci;

并使用以下数据:

|----|----------|----------|
| id | position | chart_id |
|----|----------|----------|
| .  | .        | .        |
| .  | .        | .        |
| 3  | 6        | 2        |
| 6  | 1        | 3        |
| 1  | 2        | 3        |
| 4  | 3        | 3        |
| 5  | 4        | 3        |
| 2  | 1        | 4        |
| .  | .        | .        |
| .  | .        | .        |
|----|----------|----------|

当使用对象(即不是 DQL)时,如何对 ID#3 的图表执行以下操作:

将系列 ID#1 的位置更改为 4,将系列 ID#5 的位置更改为 2? 删除系列 ID#4,然后重新排序剩余系列 ID#6、ID#1 和 ID#5 的位置,从 1、2、4 到 1、2、3?

当我尝试执行 flush() 时,我得到一个唯一约束错误。

在没有 Doctrine 的情况下进行交换操作时,我将 ID#1 位置临时存储为零,将 ID#5 位置写入 ID#1 位置(不再违反唯一约束),然后将 ID#1 位置写入ID#5 位置。

在没有Doctrine的情况下进行删除操作时,我先删除ID#4,然后使用UPDATE series SET position=position-1 WHERE chart_id=3 AND position > 2重新排序。

我认识到也许没有办法指示 Doctrine 执行此操作(尤其是交换操作)。

我是否能够添加一些 Doctrine 监听器,它只是干预选定的案例并实现我在没有 Doctrine 的情况下所做的事情?

或者也许人们不应该使用数据库级别的唯一索引并信任 Doctrine?

【问题讨论】:

可能是数据库的限制。您可以在原始 SQL 中的一个事务中进行此更改吗? @Michas 如果不使用触发器或存储过程,我不相信。与 Doctrine 一起使用时是否需要一个模式,其中所有实体修改都可以在一个事务中完成? 【参考方案1】:

某些数据库可以在事务结束时检查唯一约束。在这种情况下,Doctrine ORM 可以正常工作。看来 mysql 还不支持这个功能。

只有数据库本身才能可靠地强制执行唯一性。教义对此的支持微乎其微。

解决方法是将这些操作分为两个阶段。

    删除行或将位置更改为负数。 将位置更改为所需的数字。

在原始 SQL 中,很容易将这些操作放在一个事务中。你也可以为 Doctrine 创建监听器,但这要复杂得多。

【讨论】:

谢谢米哈斯。在事务结束时检查唯一约束是迄今为止最好的解决方案。感谢您的解决方法。

以上是关于Doctrine 可以处理多列 UNIQUE 索引吗?的主要内容,如果未能解决你的问题,请参考以下文章

Django:创建索引:非唯一,多列

使用 Doctrine 按多列排序

SQLServer之创建唯一聚集索引

SQLServer之UNIQUE约束

mysql之索引

索引和引擎小结