Kononnable / typeorm-model-generator

Generates models for TypeORM from existing database.
MIT License
1.51k stars 281 forks source link

Many-To-Many Schema Recommended Configuration #313

Closed rufustfirefly2016 closed 3 years ago

rufustfirefly2016 commented 3 years ago

This is a wonderful tool - works great on existing schema.

The only problem I seem to be having is with many-to-many relationships. Is there a recommended "best-practice" for configuring schema in many-to-many relationships?

Here's what I have - same result for both MySQL and MSSQL.

I have a table for users, roles, and a join table for both: create table roles ( id int not null primary key, name varchar(255) null, createdAt datetime not null, updatedAt datetime not null ); create table users ( id int auto_increment primary key, username varchar(255) null, email varchar(255) null, password varchar(255) null, createdAt datetime not null, updatedAt datetime not null ); create table user_role ( createdAt datetime not null, updatedAt datetime not null, roleId int not null, userId int not null, primary key (roleId, userId), constraint user_role_ibfk_1 foreign key (roleId) references roles (id) on update cascade on delete cascade, constraint user_role_ibfk_2 foreign key (userId) references users (id) on update cascade on delete cascade );

I would expect to see some reference to many-to-many in the TS code - here's my output after running typeorm-model-generator. Just default settings - nothing fancy.

Users.ts:import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { UserRole } from "./UserRole";

@Entity("users", { schema: "wheeler" }) export class Users { @PrimaryGeneratedColumn({ type: "int", name: "id" }) id: number;

@Column("varchar", { name: "username", nullable: true, length: 255 }) username: string | null;

@Column("varchar", { name: "email", nullable: true, length: 255 }) email: string | null;

@Column("varchar", { name: "password", nullable: true, length: 255 }) password: string | null;

@Column("datetime", { name: "createdAt" }) createdAt: Date;

@Column("datetime", { name: "updatedAt" }) updatedAt: Date;

@OneToMany(() => UserRole, (userRole) => userRole.user) userRoles: UserRole[]; } import { Column, Entity, OneToMany } from "typeorm"; import { UserRole } from "./UserRole";

@Entity("roles", { schema: "wheeler" }) export class Roles { @Column("int", { primary: true, name: "id" }) id: number;

@Column("varchar", { name: "name", nullable: true, length: 255 }) name: string | null;

@Column("datetime", { name: "createdAt" }) createdAt: Date;

@Column("datetime", { name: "updatedAt" }) updatedAt: Date;

@OneToMany(() => UserRole, (userRole) => userRole.role) userRoles: UserRole[]; } import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm"; import { Roles } from "./Roles"; import { Users } from "./Users";

@Index("userId", ["userId"], {}) @Entity("user_role", { schema: "wheeler" }) export class UserRole { @Column("datetime", { name: "createdAt" }) createdAt: Date;

@Column("datetime", { name: "updatedAt" }) updatedAt: Date;

@Column("int", { primary: true, name: "roleId" }) roleId: number;

@Column("int", { primary: true, name: "userId" }) userId: number;

@ManyToOne(() => Roles, (roles) => roles.userRoles, { onDelete: "CASCADE", onUpdate: "CASCADE", }) @JoinColumn([{ name: "roleId", referencedColumnName: "id" }]) role: Roles;

@ManyToOne(() => Users, (users) => users.userRoles, { onDelete: "CASCADE", onUpdate: "CASCADE", }) @JoinColumn([{ name: "userId", referencedColumnName: "id" }]) user: Users; }

Ricki-BumbleDev commented 3 years ago

AFAICT a "pure" many-to-many join table must only consist of the two foreign key columns that together also act as the combined primary key. In your case it creates a separate entity for the relationship because of the additional fields you added to the relationship (namely createdAt and updatedAt). If you'd remove them it should create your desired output. That being said, the way it's happening at the moment actually makes sense because otherwise createdAt and updatedAt would become inaccessible.