【问题标题】:TypeORM composite foreign key类型ORM复合外键
【发布时间】:2023-03-15 11:17:01
【问题描述】:

在第一类中,主键是复合的:

@Entity({
  name: 'ClassA',
  database: Constants.DATABASE_NAME,
})
export class ClassA {
  @PrimaryColumn({
    name: 'Field1',
    type: 'varchar',
    length: 4,
  })
  field1: string;

  @PrimaryColumn({
    name: 'Field2',
    type: 'varchar',
    length: 2,
  })
  field2: string;

  @PrimaryColumn({
    name: 'Field3',
    type: 'integer',
  })
  field3: number;

  @OneToMany(() => ClassB, (classB) => classB.classA)
  classB: ClassB[];
}

在另一个类中,第一个类用作外键:

@Entity({
  name: 'ClassB',
  database: Constants.DATABASE_NAME,
})
export class ClassB {

  ...

  @ManyToOne(() => ClassA, (classA) => classA.classB, {
    cascade: true,
  })
  @JoinColumn([
    { name: 'Field1', referencedColumnName: 'field1' },
    { name: 'Field2', referencedColumnName: 'field2' },
    { name: 'Field3', referencedColumnName: 'field3' },
  ])
  classA: ClassA;
}

当我使用 nest start 启动 NestJS 时,出现错误

code: "ER_FK_NO_INDEX_PARENT"
errno: 1822
sqlMessage: Failed to add the foreign key constraint. Missing index for constraint 'FK_1d19fe001872b5ee9ab545c18f8' in the referenced table 'ClassA'
sqlState: "HY000

我尝试修改ClassA表,在主键的每一列上添加索引,然后对主键的所有列进行索引,但错误还是一样。

是否可以使用 TypeORM 管理这种情况?

相关包:

  • typeorm:0.2.41
  • @nestjs/核心:8.0.0
  • @nestjs/typeorm: 8.0.3
  • mysql:2.18.1

数据库:MySQL v8.0 在 Docker 容器中 (mysql:latest)

谢谢。

编辑

似乎问题在于使用 TypeORM 不止一步创建了表。错误消息后的表格如下所示。

CREATE TABLE `ClassB` (
  `Date` datetime NOT NULL,
  `ClassAField1` varchar(4) NOT NULL,
  `ClassAField2` varchar(2) NOT NULL,
  `ClassAField3` int NOT NULL,
  PRIMARY KEY (`Date`,`ClassAField1`,`ClassAField2`,`ClassAField3`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

错误发生在以下查询中,其中 TypeORM 尝试添加外键:

ALTER TABLE `ClassB`
ADD CONSTRAINT `FK_479eaf779e0d5f0d0a83bb9e30d`
FOREIGN KEY (`ClassAField1`, `ClassAField2`, `ClassAField3`)
REFERENCES `LandPlots`(`Field1`,`Field2`,`Field3`) ON DELETE NO ACTION ON UPDATE NO ACTION

我做了一些测试,以下查询按预期工作:

CREATE TABLE `ClassB` (
  `Date` datetime NOT NULL,
  `ClassAField1` varchar(4) NOT NULL,
  `ClassAField2` varchar(2) NOT NULL,
  `ClassAField3` int NOT NULL,
  PRIMARY KEY (`Date`),
  KEY `FK_e6b2926df2c758d8d8810e4345a` (`ClassAField1`, `ClassAField2`, `ClassAField3`),
  CONSTRAINT `FK_e6b2926df2c758d8d8810e4345a`
  FOREIGN KEY (`ClassAField1`, `ClassAField2`, `ClassAField3`)
  REFERENCES `ClassA` (`Field1`, `Field2`, `Field3`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

区别似乎是KEY `FK_e6b2926df2c758d8d8810e4345a` (`ClassAField1`, `ClassAField2`, `ClassAField3`),这行,TypeORM生成的SQL中缺少了这一行。

MySQL documentationKEY 语句是INDEX 的同义词:

 KEY | INDEX

KEY is normally a synonym for INDEX. The key attribute PRIMARY KEY can also be specified as just KEY when given in a column definition. This was implemented for compatibility with other database systems. 

所以我尝试将 @Index 装饰器添加到列中,但它没有改变任何东西。我添加了所有已执行查询的日志记录,似乎 @Index 装饰器没有执行,所以,也许 @JoinColumn 装饰器优先于它? (试图以不同的方式对装饰器进行排序以防万一)。

我能想到的使用 TypeORM 的唯一解决方案是生成所有 3 个主键的散列或串联,并使其成为唯一的主键。两个缺点:插入前解析实体时的数据重复和 CPU 使用率。

如果有人有更好的建议,我想听听。

编辑@RickJames 提出的以下 2 个问题

更新声明:

UPDATE cb
SET cb.SomeField = 'NewValue'
FROM ClassB cb
JOIN ClassA ca
ON cb.Field1 = ca.Field1
AND cb.Field2 = ca.Field2
AND cb.Field3 = ca.Field3
WHERE ca.SomeOtherField LIKE '%partialvalue%'
AND cb.otherPK = 1

【问题讨论】:

  • 在帮助 how to 索引之前,我想先评论一下 what to 索引。请提供您希望为其添加索引的示例SELECTs/UPDATEs/DELETEs`。
  • 你好@RickJames。复合外键上的索引(第 6 个代码 sn-p 中的“KEY”),否则,根据我的测试,它最终可以工作。但是TypeORM似乎并没有同时处理主键+ \@ManyToOne + \@JoinColumn + \@Index,它只处理PK,然后尝试将列更改为复合FK,这不起作用。我添加了一条更新声明,但我不确定我是否很好地理解了您的问题。
  • <frustration> 我的太多时间都花在尝试解决产品(ORM 等)中试图对用户隐藏 MySQL 的缺陷上。用户最终不得不理解 both 抽象并编写“原始”查询以获得性能。 </frustration>
  • @RickJames 我同意你的看法。对于简单的操作,ORM 很酷,而且我还想通过不操作 SQL 来使流程更干净,但最终一项需要几个小时的工作却占用了我个人的几天时间。

标签: mysql typescript nestjs typeorm


【解决方案1】:

该 UPDATE 可能会受益于其中一些索引:

cb:  INDEX(otherPK, Field1, Field2, Field3)
ca:  INDEX(Field1, Field2, Field3, SomeOtherField)

【讨论】:

  • 感谢您的帮助。它没有解决 TypeORM 特定的问题,但我认为最终它是一个 TypeORM 限制,使用这个 ORM 无法解决。我接受您的回答,因为这将有助于优化数据库查询。
猜你喜欢
  • 1970-01-01
  • 2020-08-12
  • 2014-01-18
  • 2019-07-02
  • 1970-01-01
  • 2012-08-04
  • 2020-08-14
  • 1970-01-01
相关资源
最近更新 更多