question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to delete on cascade?

See original GitHub issue

Issue type:

[ ] question [x] bug report [ ] feature request [ ] documentation issue

Database system/driver:

[ ] cordova [ ] mongodb [ ] mssql [ x] mysql / mariadb [ ] oracle [ ] postgres [ ] sqlite [ ] sqljs [ ] react-native

TypeORM version:

[ x] latest [ ] @next [ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem: I have a Fulfillment entity that has a @OneToOne Address relation. When I create a Fulfillment, I only need to save once because the Address is created on cascade. When I delete the Fulfillment, I must delete Fulfillment and Address in two separate removes, because it is not deleted when Fulfillment is deleted alone. Why is this?

import Fulfillment from "../src/entity/Fulfillment";
import { createConnection, Connection } from "typeorm";
import Branch from "../src/entity/Branch";
import Address from "../src/entity/Address";

// Use for reference on writing good tests: https://github.com/typeorm/typeorm/blob/master/test/functional/repository/clear/repository-clear.ts
// https://github.com/typeorm/typeorm/blob/master/test/github-issues/70/issue-70.ts
describe("Fulfillment", () => {
    let connection: Connection;

    beforeAll(async () => {
        connection = await createConnection();
    });

    beforeEach(async () => {
        await connection.synchronize(true);
    });

    afterAll(async () => {
        await connection.close();
    });

    it("should create an Address", async () => {
        const address = new Address();
        address.addressLine1 = "114 5th Ave";
        address.city = "New York";
        address.stateCode = "NY";
        address.postalCode = "10011";

        const fulfillment = new Fulfillment();
        fulfillment.branchDid = "41970";
        fulfillment.deliveryInstructions = "test";
        fulfillment.address = address;
        await connection.manager.save(fulfillment);
        expect(fulfillment.fulfillmentId).toBe(1);
        expect(fulfillment.address.addressId).toBe(1);
    });

    it("should delete Address on Cascade" , async () => {
        const address = new Address();
        address.addressLine1 = "114 5th Ave";
        address.city = "New York";
        address.stateCode = "NY";
        address.postalCode = "10011";

        const fulfillment = new Fulfillment();
        fulfillment.branchDid = "41970";
        fulfillment.deliveryInstructions = "test";
        fulfillment.address = address;
        await connection.manager.save(fulfillment);
        // check if fulfillment has been saved
        const loadedFullfillments = await connection.manager.find(Fulfillment);
        expect(loadedFullfillments.length).toBe(1);

        await connection.manager.remove(fulfillment);
        // TODO: This shouldn't be necessary but it's fine for now.
        await connection.manager.remove(fulfillment.address);
        const loadedFulfillmentsAfterDelete = await connection.manager.find(Fulfillment);
        expect(loadedFulfillmentsAfterDelete.length).toBe(0);
        const loadedAddressesAfterDelete = await connection.manager.find(Address);
        expect(loadedAddressesAfterDelete.length).toBe(0);
    });
});

import Address from "./Address";
import { Timestamp, Column, OneToOne, PrimaryGeneratedColumn, Entity, JoinColumn } from "typeorm";
import Order from "./Order";

// KLT: TODO
// Need this to transform string to date from json.
// Example: https://github.com/typeorm/typeorm/blob/master/test/functional/columns/value-transformer/value-transformer.ts
// Example: https://github.com/typeorm/typeorm/blob/master/test/github-issues/1140/entity/Post.ts
// const transformer = {
//     from(value: string): Date {
//         return new Date();
//     },
//     to(value: number): Date {
//         return new Date(value);
//     }
// };

@Entity()
class Fulfillment {
    @PrimaryGeneratedColumn()
    fulfillmentId: number;

    @Column("varchar", { default: null })
    branchDid: string;

    @Column("timestamp", { default: () => "CURRENT_TIMESTAMP" })
    deliveryBeginTime: Timestamp;

    @Column("timestamp", { default: () => "CURRENT_TIMESTAMP" })
    deliveryEndTime: Timestamp;

    @OneToOne(type => Address, { cascade: true, onDelete: "CASCADE", primary: true})
    address: Address;

    @Column("varchar", { length: 2083, default: null })
    trackingLink: string;

    @Column("varchar", { default: null })
    deliveryInstructions: string;

    @Column("varchar", { length: 50, default: "Postmates" })
    deliveryMethod: string;
}

export default Fulfillment;

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
class Address {

    @PrimaryGeneratedColumn()
    addressId: number;

    @Column()
    addressLine1: string;

    @Column({default: null})
    addressLine2: string;

    @Column()
    city: string;

    @Column()
    stateCode: string;

    @Column()
    postalCode: string;
}

export default Address;

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:9

github_iconTop GitHub Comments

7reactions
filipjnccommented, May 1, 2018

Have you tried adding the decorator @JoinColumn() below @OneToOne? This will add the column addressId on the Fulfillment table, which I think is a prerequisite for cascade delete to work.

@OneToOne(type => Address, { cascade: true, onDelete: "CASCADE", primary: true})
@JoinColumn()
address: Address;

@JoinColumn() can be used on either side of the relation, depending in which table you want to store the anchor id. Important: it must be set only on one side of the relation, see the docs.

1reaction
Kurrycommented, May 3, 2018

@filipjnc I think I figured it out. Does this intuitively make sense?

import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from "typeorm";
import Profile from "./Profile";

@Entity()
class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column({ default: null })
    name: string;

    @OneToOne(type => Profile, { cascade: true})
    profile: Profile;
}

export default User;

import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from "typeorm";
import User from "./User";

@Entity()
class Profile {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    gender: string;

    @Column()
    photo: string;

    @OneToOne(type => User, { cascade: true, onDelete: "CASCADE" })
    @JoinColumn()
    user: User;
}

export default Profile;

import { createConnection, Connection } from "typeorm";
import Profile from "../src/entity/Profile";
import User from "../src/entity/User";

describe("Profile", () => {

    let connection: Connection;

    beforeAll(async () => {
        connection = await createConnection();
    });

    beforeEach(async () => {
        await connection.synchronize(true);
    });

    afterAll(async () => {
        await connection.close();
    });

    it("should create associated entities on cascade", async () => {
        const profile = new Profile();
        profile.gender = "male";
        profile.photo = "me.jpg";
        const user = new User();
        user.profile = profile;
        profile.user = user;
        await connection.manager.save(user);
        await connection.manager.remove(user);
        const profiles = await connection.manager.find(Profile);
        const users = await connection.manager.find(User);
        expect(users).toHaveLength(0);
        expect(profiles).toHaveLength(0);
    });
});

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using the ON DELETE CASCADE Option - IBM
Use the ON DELETE CASCADE option to specify whether you want rows deleted in a child table when corresponding rows are deleted in...
Read more >
MySQL - ON DELETE CASCADE Constraint - GeeksforGeeks
ON DELETE CASCADE constraint is used in MySQL to delete the rows from the child table automatically, when the rows from the parent...
Read more >
How to add 'ON DELETE CASCADE' in ALTER TABLE ...
Choose your constraint on the left side (if there are more than one). Then on the right side, collapse "INSERT And UPDATE Specification"...
Read more >
SQL Server: Foreign Keys with cascade delete - TechOnTheNet
A foreign key with cascade delete means that if a record in the parent table is deleted, then the corresponding records in the...
Read more >
Good explanation of cascade (ON DELETE/UPDATE) behavior
SQL:2011 Spec · ON DELETE CASCADE : if a row of the referenced table is deleted, then all matching rows in the referencing...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found