Search code examples
javascriptpostgresqlnestjstypeorm

Not able to delete records when there are related records in other table with foreign key


Getting the following error, tried onDelete: 'CASCADE' on the @ManyToOne relation

[ExceptionsHandler] update or delete on table "resource" violates foreign key constraint "resource_skill_resource_id_fk" on table "resource_skill"

Resorce Entity

import {
    BaseEntity,
    Column,
    Entity,
    Index,
    JoinColumn,
    JoinTable,
    ManyToMany,
    ManyToOne,
    OneToMany,
    OneToOne,
    PrimaryColumn,
    PrimaryGeneratedColumn,
    RelationId
} from 'typeorm';
import { Organization } from './organization';
import { AreaResource } from './area_resource';
import { Client } from './client';
import { EventResource } from './event_resource';
import { LocationResource } from './location_resource';
import { LocationResourceAssignment } from './location_resource_assignment';
import { ResourceSkill } from './resource_skill';
import { TeamResource } from './team_resource';

@Entity('resource', { schema: 'cccalpha1' })
export class Resource {

    @Column('text', {
        nullable: true,
        name: 'first_name'
    })
    first_name: string | null;


    @Column('text', {
        nullable: true,
        name: 'email'
    })
    email: string | null;


    @Column('text', {
        nullable: true,
        name: 'background_color'
    })
    background_color: string | null;


    @Column('text', {
        nullable: true,
        name: 'phone'
    })
    phone: string | null;


    @Column('text', {
        nullable: true,
        name: 'last_name'
    })
    last_name: string | null;


    @Column('text', {
        nullable: true,
        name: 'font_color'
    })
    font_color: string | null;


    @Column('text', {
        nullable: true,
        name: 'full_name'
    })
    full_name: string | null;


    @Column('text', {
        nullable: true,
        name: 'prefix'
    })
    prefix: string | null;


    @Column('text', {
        nullable: true,
        name: 'middle_name'
    })
    middle_name: string | null;


    @Column('text', {
        nullable: true,
        name: 'full_name_last_first'
    })
    full_name_last_first: string | null;


    @Column('boolean', {
        nullable: true,
        name: 'inactive'
    })
    inactive: boolean | null;


    @Column('text', {
        nullable: true,
        name: 'suffix'
    })
    suffix: string | null;


    @Column('text', {
        nullable: true,
        name: 'fax_number'
    })
    fax_number: string | null;


    @Column('boolean', {
        nullable: true,
        name: 'email_schedule_alert'
    })
    email_schedule_alert: boolean | null;


    @Column('text', {
        nullable: true,
        name: 'cell_number'
    })
    cell_number: string | null;


    @Column('boolean', {
        nullable: true,
        name: 'text_schedule_alert'
    })
    text_schedule_alert: boolean | null;


    @Column('text', {
        nullable: true,
        name: 'email_cell_address'
    })
    email_cell_address: string | null;


    @Column('uuid', {
        nullable: false,
        primary: true,
        default: () => 'uuid_generate_v4()',
        name: 'id'
    })
    id: string;


    @ManyToOne(type => Organization, organization => organization.resource, {})
    @JoinColumn({name: 'organization_id'})
    organization: Organization | null;


    @OneToMany(type => AreaResource, area_resource => area_resource.resource, { cascade: true })
    area_resource: AreaResource[];


    @OneToMany(type => Client, client => client.resource)
    client: Client[];


    @OneToMany(type => EventResource, event_resource => event_resource.resource)
    event_resource: EventResource[];


    @OneToMany(type => LocationResource, location_resource => location_resource.resource)
    location_resource: LocationResource[];


    @OneToMany(type => LocationResourceAssignment, location_resource_assignment => location_resource_assignment.resource)
    location_resource_assignment: LocationResourceAssignment[];


    @OneToMany(type => ResourceSkill, resource_skill => resource_skill.resource, { eager: true, cascade: true })
    resource_skill: ResourceSkill[];


    @OneToMany(type => TeamResource, team_resource => team_resource.resource)
    team_resource: TeamResource[];

}

Resource Skill Entity

import {
    BaseEntity,
    Column,
    Entity,
    Index,
    JoinColumn,
    JoinTable,
    ManyToMany,
    ManyToOne,
    OneToMany,
    OneToOne,
    PrimaryColumn,
    PrimaryGeneratedColumn,
    RelationId
} from 'typeorm';
import { Resource } from './resource';
import { Skill } from './skill';

@Entity('resource_skill', { schema: 'cccalpha1' })
export class ResourceSkill {

    @Column('uuid', { 
        nullable: false,
        primary: true,
        default: () => 'uuid_generate_v4()',
        name: 'id'
    })
    id: string;


    @ManyToOne(type => Resource, resource => resource.resource_skill, { onDelete: 'CASCADE' })
    @JoinColumn({ name: 'resource_id' })
    resource: Resource | null;


    @ManyToOne(type => Skill, skill => skill.resource_skill, { onDelete: 'CASCADE' })
    @JoinColumn({ name: 'skill_id' })
    skill: Skill | null;

}

Solution

  • I did three things and deletion works great now.

    1. Dropped all tables from the schema (Not sure if it was necessary)
    2. Changed the way the primary columns are declared in the Entity as following,
    // Old way
    @Column('uuid', { 
       nullable: false,
       primary: true,
       default: () => 'uuid_generate_v4()',
       name: 'id'
    })
    id: string;
    

    to

    // New way
    @PrimaryGeneratedColumn('uuid')
    id: string;
    
    1. Set synchronize to true in the module so that the DB is synchronized with the Entity

    synchronize - Synchronizes database schema. When synchronize: true is set in connection options it calls this method. Usually, you call this method when your application is shutting down.

    P.S: May be the first step is not necessary but I was already down the path where I dropped the tables