node.jstypescriptmany-to-manytypeormnode.js-typeorm

Node.JS Typescript TypeORM ManyToMany `save` overrides previous relation instead of adding a new one


I have the following typescript TypeORM model:

@Entity()
export class Student extends EntityBase {
    @Column({ length: 256 })
    public firstName: string

    @Column({ length: 256 })
    public lastName: string

    @Column({ unique: true, length: 256 })
    public email: string

    @Column()
    public isSuspended: boolean

    @ManyToMany((type) => Teacher, (teacher) => teacher.students, { cascade: true })
    @JoinTable()
    public teachers: Teacher[]

    constructor(first: string, last: string, email: string, isSuspended?: boolean) {
        super();
        this.firstName = first;
        this.lastName = last;
        this.email = email;
        this.isSuspended = isSuspended ?? false;
    }
}
@Entity()
export class Teacher extends EntityBase {
    @Column({ length: 256 })
    public firstName: string

    @Column({ length: 256 })
    public lastName: string

    @Column({ unique: true, length: 256 })
    public email: string

    @ManyToMany((type) => Student, (student) => student.teachers)
    public students: Student[]

    constructor(first: string, last: string, email: string) {
        super();
        this.firstName = first;
        this.lastName = last;
        this.email = email;
    }
}

This is what the StudentRepository.AddTeacher does:

    public async AddTeacher (student: Student, teacher: Teacher): Promise<Student | null> {
        if (teacher && student) {
            try {
                student.teachers.push(teacher);
                return await this.Update(student);
            } catch (e) { console.log(e); }
        }
        return null;
    }

The following code should iterate through the list of students to add to teacher collection but it fails to achieve that:

                for (let i of request.Students) {
                    let s: Student | null = await this._studentRepository.GetByEmail(i.email);
                    if (s !== null && (t!.students.length === 0 || t!.students.find(ss => ss.email == s.email) == null)) {
                        this._logger.Log(LogLevels.debug, `Adding student: ${JSON.stringify(s)} to teacher ${JSON.stringify(t)}`);
                        if (!s.teachers.some(i => i.id === t.id)) {
                            let student = await this._studentRepository.AddTeacher(s, t);
                            this._logger.Log(LogLevels.debug, `student: ${JSON.stringify(student)}`);
                            if (student !== null)
                                count++;
                            else {
                                this._logger.Log(LogLevels.error, `Failed to add student ${s.email} to teacher ${t.email}`);
                                errors.push(new Error("", `Failed to add student ${s.email} to teacher ${t.email}`));
                            }
                        } else {
                            this._logger.Log(LogLevels.error, `Skip existing Student ${i.email} <-> teacher ${t.email}`);
                            errors.push(new Error("", `Skip existing Student ${i.email} <-> teacher ${t.email}`));
                        }
                    } else if (s === null) {
                        this._logger.Log(LogLevels.error, `Invalid student ${i.email}`);
                        errors.push(new Error("", `Invalid student ${i.email}`));
                    } else {
                        this._logger.Log(LogLevels.error, `Failed to add student ${i.email} to teacher ${t.email}`);
                        errors.push(new Error("", `Failed to add student ${i.email} to teacher ${t.email}`));
                    }
                };

Input Post data:

{
  "teacher": {
      "email": "teacher1@gmail.com"
   },
  "students":
    [
      "student1@gmail.com",
      "student2@gmail.com"
    ]
}

Log:

2024-07-07 13:44:38:4438 debug: Adding 2 students to teacher teacher1@gmail.com...
2024-07-07 13:44:38:4438 debug: Adding student: {"id":1,"created":"2024-07-07T05:44:33.716Z","modified":"2024-07-07T05:44:33.716Z","firstName":"One","lastName":"Student","email":"student1@gmail.com","isSuspended":false,"teachers":[]} to teacher {"id":1,"created":"2024-07-07T05:44:33.736Z","modified":"2024-07-07T05:44:33.736Z","firstName":"One","lastName":"Teacher","email":"teacher1@gmail.com","students":[]}
2024-07-07 13:44:38:4438 debug: student: {"id":1,"created":"2024-07-07T05:44:33.716Z","modified":"2024-07-07T05:44:33.716Z","firstName":"One","lastName":"Student","email":"student1@gmail.com","isSuspended":false,"teachers":[{"id":1,"created":"2024-07-07T05:44:33.736Z","modified":"2024-07-07T05:44:33.736Z","firstName":"One","lastName":"Teacher","email":"teacher1@gmail.com","students":[]}]}
2024-07-07 13:44:38:4438 debug: Adding student: {"id":2,"created":"2024-07-07T05:44:33.731Z","modified":"2024-07-07T05:44:33.731Z","firstName":"Two","lastName":"Student","email":"student2@gmail.com","isSuspended":false,"teachers":[]} to teacher {"id":1,"created":"2024-07-07T05:44:33.736Z","modified":"2024-07-07T05:44:33.736Z","firstName":"One","lastName":"Teacher","email":"teacher1@gmail.com","students":[]}
2024-07-07 13:44:38:4438 debug: student: {"id":2,"created":"2024-07-07T05:44:33.731Z","modified":"2024-07-07T05:44:33.731Z","firstName":"Two","lastName":"Student","email":"student2@gmail.com","isSuspended":false,"teachers":[{"id":1,"created":"2024-07-07T05:44:33.736Z","modified":"2024-07-07T05:44:33.736Z","firstName":"One","lastName":"Teacher","email":"teacher1@gmail.com","students":[]}]}

I check the database, it seems that the later data (student2) overrides the previous relation (student1) instead of adding a new relation.


Solution

  • Code issue. Refactor it to load both entities in the loop solve the problem.