Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeORM - updating many to many relation throws column does not exist error

I have a user entity that can contain multiple products. When I update my user enity with new products the following error gets thrown: column "userId" of relation "user" does not exist. I do not know where the userId column at the user entity comes from. When I skip updating the products everything works fine. I already tried dopping and recreating all tables and I also added a bidirectional relation to product.ts but the error is still there. After a long search I still do not know where my mistake is.

My entites:

User.ts

import {
  BaseEntity,
  Entity,
  Column,
  PrimaryGeneratedColumn,
  OneToOne,
  ManyToMany,
  JoinColumn,
  JoinTable,
  BeforeInsert,
  BeforeUpdate,
  Unique,
  CreateDateColumn,
  UpdateDateColumn,
  ManyToOne,
  Index,
} from 'typeorm';
import { hash } from 'argon2';
import Type from './Type';
import Address from './Address';
import Product from './Product';

export enum UserRoles {
  GAST,
  ANBIETER,
  MODERATOR,
  ADMIN,
}

@Entity()
@Unique(['email'])
export default class User extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 100, nullable: true })
  displayName: string;

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

  @Column({ select: false })
  password: string;

  @ManyToOne(() => Type)
  @JoinColumn()
  type: Type;

  @Column({
    type: 'enum',
    enum: UserRoles,
    default: UserRoles.GAST,
  })
  role: UserRoles;

  @OneToOne(() => Address, { nullable: true })
  @JoinColumn()
  address: Address;

  @Column({ nullable: true })
  website: string;

  @Column({ nullable: true })
  phoneNumber: string;

  @Column({ length: 250, nullable: true })
  description: string;

  @Column({ length: 250, nullable: true })
  notes: string;

  @Column({ default: false })
  verified: boolean;

  @Column({ default: true })
  visible: boolean;

  @ManyToMany(() => Product)
  @JoinTable({ name: 'user_products' })
  products: Product[];

  // https://github.com/typeorm/typeorm/issues/2651
  @CreateDateColumn({ update: false })
  createdAt: Date;

  @UpdateDateColumn({ update: false })
  updatedAt: Date;

  @BeforeInsert()
  @BeforeUpdate()
  async hashPassword() {
    if (this.password && !this.password.startsWith('$argon2')) {
      this.password = await hash(this.password);
    }
  }

  static findByEmailWithPassord(email: string): Promise<User> {
    return this.createQueryBuilder('user')
      .where('user.email = :email', { email })
      .addSelect('user.password')
      .getOne();
  }
}

Product.ts

import {
  BaseEntity,
  Entity,
  Column,
  PrimaryGeneratedColumn,
  CreateDateColumn,
  UpdateDateColumn,
} from 'typeorm';

@Entity()
export default class Product extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 100 })
  name: string;

  // https://github.com/typeorm/typeorm/issues/2651
  @CreateDateColumn({ update: false })
  createdAt: Date;

  @UpdateDateColumn({ update: false })
  updatedAt: Date;
}

The update routine:

const updateUser: User = await User.findOne(userId, {
  relations: ['address', 'products'],
});

updateUser.displayName = req.body.displayName;
updateUser.email = req.body.email;
updateUser.website = req.body.website;
updateUser.phoneNumber = req.body.phoneNumber;
updateUser.description = req.body.description;
updateUser.visible = req.body.visible;

if (req.body.address) {
  const address: Address = updateUser.address || new Address();
  address.company = req.body.address.company;
  address.address1 = req.body.address.address1;
  address.address2 = req.body.address.address2;
  address.address3 = req.body.address.address3;
  address.code = req.body.address.code;
  address.city = req.body.address.city;
  address.state = req.body.address.state;
  address.country = req.body.address.country;

  if (!updateUser.address) {
    await address.save();
  } else {
    await Address.update(address.id, address);
  }

  updateUser.address = address;
}

if (req.body.products) {
  const products = await Product.find({
    where: {
      id: In(req.body.products),
    },
  });

  updateUser.products = req.body.products; // when removing this everything works fine
}

await User.update(userId, updateUser);

This error gets thrown:

message: column "userId" of relation "user" does not exist
name: QueryFailedError
code: 42703
file: analyze.c
line: 2333
routine: transformUpdateTargetList
query:
UPDATE "user"
SET "id"          = $2,
    "displayName" = $3,
    "email"       = $4,
    "role"        = $5,
    "website"     = $6,
    "phoneNumber" = $7,
    "description" = $8,
    "notes"       = $9,
    "verified"    = $10,
    "visible"     = $11,
    "addressId"   = $12,
    "userId"      = $13, -- where does this come from?
    "updatedAt"   = CURRENT_TIMESTAMP
WHERE "id" IN ($1)
like image 697
Tim Avatar asked Apr 17 '20 08:04

Tim


1 Answers

The answer is way simpler than thought I just have to run await updateUser.save(); instead of await User.update(userId, updateUser);. I do not know why I did it that way.

like image 90
Tim Avatar answered Nov 14 '22 23:11

Tim