TypeOrm OneToOne 关系导致不需要的列嵌入

Posted

技术标签:

【中文标题】TypeOrm OneToOne 关系导致不需要的列嵌入【英文标题】:TypeOrm OneToOne relation causing unwanted embedding of columns 【发布时间】:2020-10-10 14:09:29 【问题描述】:

我有 2 个实体具有 OneToOne 关系(用户和 CodUser),并且用户具有 JoinColumn。当 TypeOrm 将此模式同步到数据库时,它会在用户中创建 CodUser 列,因此它嵌入 CodUser 并为 CodUser 实体创建一个单独的表。

我想要的是 2 个实体是分开的,并且 CodUser 中的列仅出现在我的(Postgres)数据库的 CodUser 表中。 我相信这正是 TypeOrm 对demonstrate OneToOne 关系使用的模式,但似乎有些不对劲。

user.entity.ts

@Entity()
export class User 
@PrimaryGeneratedColumn()
id: number;

@Column( unique: true )
email: string;

@Column( nullable: true )
avatar: string;

@Column()
password: string;

@Column(
    type: "text",
    array: true,
    nullable: false,
    default: `$Role.Player`
)
roles: Role[];

@OneToOne((type) => CodUser, 
    cascade: true
)
@JoinColumn()
codUser: CodUser;

@Column( default: 0 )
invalidLoginAttempts: number;

@Column( nullable: true )
activationCode: string;

@Column( nullable: false, default: false )
active: boolean;

@Column( nullable: true )
lockedUntil: Date;

@Column( nullable: true )
lastLoginAttemptAt: Date;

@OneToMany((type) => ChallangeUser, (challangeUser) => challangeUser.user)
challanges: ChallangeUser[];

@CreateDateColumn( type: "timestamp" )
createdAt: Date;

@UpdateDateColumn( type: "timestamp" )
updatedAt: Date;

constructor(email: string, username: string, platform: Platform, password: string) 
    this.email = email;
    this.codUser = new CodUser(username, platform);
    this.password = password;


cod-user.entity.ts

@Entity()
export class CodUser 
@PrimaryGeneratedColumn()
id: string;

@Column( unique: true )
username: string;

@Column( nullable: false )
platform: Platform;

@Column( type: "jsonb", nullable: true )
recentWarzoneMatches: object[];

@Column( nullable: true )
recentWarzoneMatchesSyncedAt: Date;

constructor(username: string, platform: Platform) 
    this.username = username;
    this.platform = platform;


结果:

同步交易:

query: START TRANSACTION
query: SELECT * FROM current_schema()
query: SELECT * FROM "information_schema"."tables" WHERE ("table_schema" = 'public' AND "table_name" = 'challange') OR ("table_schema" = 'public' AND "table_name" = 'challange_user') OR ("table_schema" = 'public' AND "table_name" = 'cod_user') OR ("table_schema" = 'public' AND "table_name" = 'user') OR ("table_schema" = 'public' AND "table_name" = 'user') OR ("table_schema" = 'public' AND "table_name" = 'challange') OR ("table_schema" = 'public' AND "table_name" = 'challange_user')
query: SELECT *, ('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype" AS "regtype" FROM "information_schema"."columns" WHERE ("table_schema" = 'public' AND "table_name" = 'challange') OR ("table_schema" = 'public' AND "table_name" = 'challange_user') OR ("table_schema" = 'public' AND "table_name" = 
'cod_user') OR ("table_schema" = 'public' AND "table_name" = 'user') OR ("table_schema" = 'public' AND "table_name" = 'user') OR ("table_schema" = 'public' AND "table_name" = 'challange') OR ("table_schema" = 'public' AND "table_name" = 'challange_user')
query: SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "cnst"."conname" AS "constraint_name", pg_get_constraintdef("cnst"."oid") AS "expression", CASE "cnst"."contype" WHEN 'p' THEN 'PRIMARY' WHEN 'u' THEN 'UNIQUE' WHEN 'c' THEN 'CHECK' WHEN 'x' THEN 'EXCLUDE' END AS "constraint_type", "a"."attname" AS "column_name" FROM "pg_constraint" "cnst" INNER JOIN "pg_class" "t" ON "t"."oid" = "cnst"."conrelid" INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "cnst"."connamespace" LEFT JOIN "pg_attribute" "a" ON "a"."attrelid" = "cnst"."conrelid" AND "a"."attnum" = ANY ("cnst"."conkey") WHERE "t"."relkind" = 'r' AND (("ns"."nspname" = 'public' AND "t"."relname" = 'challange') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'challange_user') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'cod_user') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'user') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'user') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'challange') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'challange_user'))
query: SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", "types"."typname" AS "type_name" FROM "pg_class" "t" INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" WHERE "t"."relkind" = 'r' AND "cnst"."contype" IS NULL AND (("ns"."nspname" = 'public' AND "t"."relname" = 'challange') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'challange_user') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'cod_user') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'user') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'user') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'challange') OR ("ns"."nspname" = 'public' AND "t"."relname" = 'challange_user'))
query: SELECT "con"."conname" AS "constraint_name", "con"."nspname" AS "table_schema", "con"."relname" AS "table_name", "att2"."attname" AS "column_name", "ns"."nspname" AS "referenced_table_schema", "cl"."relname" AS "referenced_table_name", "att"."attname" AS "referenced_column_name", "con"."confdeltype" AS "on_delete", "con"."confupdtype" AS "on_update", "con"."condeferrable" AS "deferrable", "con"."condeferred" AS "deferred" FROM ( SELECT UNNEST ("con1"."conkey") AS "parent", UNNEST ("con1"."confkey") AS "child", "con1"."confrelid", "con1"."conrelid", "con1"."conname", "con1"."contype", "ns"."nspname", "cl"."relname", "con1"."condeferrable", CASE WHEN "con1"."condeferred" THEN 'INITIALLY DEFERRED' ELSE 'INITIALLY IMMEDIATE' END as condeferred, CASE "con1"."confdeltype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confdeltype", CASE "con1"."confupdtype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confupdtype" FROM "pg_class" "cl" INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" INNER JOIN "pg_constraint" "con1" ON "con1"."conrelid" = "cl"."oid" WHERE "con1"."contype" = 'f' AND (("ns"."nspname" = 'public' AND "cl"."relname" = 'challange') OR ("ns"."nspname" = 'public' AND "cl"."relname" = 'challange_user') OR ("ns"."nspname" = 'public' AND "cl"."relname" = 'cod_user') OR ("ns"."nspname" = 'public' AND "cl"."relname" = 'user') OR ("ns"."nspname" = 'public' AND "cl"."relname" = 'user') OR ("ns"."nspname" = 'public' AND "cl"."relname" = 'challange') OR ("ns"."nspname" = 'public' AND "cl"."relname" = 'challange_user')) ) "con" INNER JOIN "pg_attribute" "att" ON "att"."attrelid" = "con"."confrelid" AND "att"."attnum" = "con"."child" INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" INNER JOIN "pg_attribute" "att2" ON "att2"."attrelid" = "con"."conrelid" AND "att2"."attnum" = "con"."parent"
query: SELECT * FROM "information_schema"."tables" WHERE "table_schema" = current_schema() AND "table_name" = 'typeorm_metadata'
query: ALTER TABLE "user" DROP CONSTRAINT "FK_c8521799e9a87cab5983c57ba1c"
query: ALTER TABLE "challange_user" DROP CONSTRAINT "FK_0c9f41f81c2f9cadd1154c90499"
query: ALTER TABLE "challange" DROP COLUMN "userLimit"
query: ALTER TABLE "user" DROP CONSTRAINT "UQ_c8521799e9a87cab5983c57ba1c"
query: ALTER TABLE "user" DROP COLUMN "custom_cod_user"
query: ALTER TABLE "user" DROP CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb"
query: ALTER TABLE "user" DROP COLUMN "username"
query: ALTER TABLE "user" DROP COLUMN "platform"
query: ALTER TABLE "user" DROP COLUMN "recentWarzoneMatches"
query: ALTER TABLE "user" DROP COLUMN "recentWarzoneMatchesSyncedAt"
query: ALTER TABLE "user" DROP COLUMN "roles"
query: ALTER TABLE "challange" DROP COLUMN "slots"
query: ALTER TABLE "challange_user" DROP COLUMN "isConfirmed"
query: ALTER TABLE "challange_user" DROP COLUMN "challangeId"
query: ALTER TABLE "challange" ADD "slots" integer NOT NULL DEFAULT -1
query: ALTER TABLE "challange_user" ADD "isConfirmed" boolean NOT NULL DEFAULT false
query: ALTER TABLE "challange_user" ADD "challangeId" uuid
query: ALTER TABLE "user" ADD "roles" text array NOT NULL DEFAULT 'player'::text[]
query: ALTER TABLE "user" ADD "codUserId" integer
query: ALTER TABLE "user" ADD CONSTRAINT "UQ_4ee803ddc575ea07a96a0aa5f40" UNIQUE ("codUserId")
query: ALTER TABLE "user" ADD "username" character varying NOT NULL
query: ALTER TABLE "user" ADD CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username")
query: ALTER TABLE "user" ADD "platform" character varying NOT NULL
query: ALTER TABLE "user" ADD "recentWarzoneMatches" jsonb
query: ALTER TABLE "user" ADD "recentWarzoneMatchesSyncedAt" TIMESTAMP
query: ALTER TABLE "challange" ADD "userLimit" integer NOT NULL DEFAULT -1
query: ALTER TABLE "challange_user" ADD CONSTRAINT "FK_0c9f41f81c2f9cadd1154c90499" FOREIGN KEY ("challangeId") REFERENCES "challange"("id") ON DELETE NO ACTION ON UPDATE NO ACTION
query: ALTER TABLE "user" ADD CONSTRAINT "FK_4ee803ddc575ea07a96a0aa5f40" FOREIGN KEY ("codUserId") REFERENCES "cod_user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION
query: COMMIT

【问题讨论】:

【参考方案1】:

删除我的 NestJS 项目中的 /dist 文件夹解决了这个问题,这似乎毕竟是一个 NestJS 问题。

【讨论】:

以上是关于TypeOrm OneToOne 关系导致不需要的列嵌入的主要内容,如果未能解决你的问题,请参考以下文章

TypeORM:如何查询与字符串数组输入的多对多关系以查找所有字符串都应存在于相关实体列中的实体?

TypeORM 更新实体/表

如何创建如何在 typeorm 中创建多对多关系,[NestJS]

何时使用/不使用,@OneToOne 和 @ManyToOne

Laravel 5.6 OneToOne 关系不起作用

ew@OneToOne 与不使用 JOIN 的播放框架的关系