typeorm / typeorm

ORM for TypeScript and JavaScript. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Electron platforms.
http://typeorm.io
MIT License
34.02k stars 6.27k forks source link

@BeforeInsert not firing on getRepository(<entity>).save() #5493

Open nealdyrkacz opened 4 years ago

nealdyrkacz commented 4 years ago

Issue type:

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

Database system/driver:

[ ] cordova [ ] mongodb [ ] mssql [ ] mysql / mariadb [ ] oracle [X ] postgres [ ] cockroachdb [ ] sqlite [ ] sqljs [ ] react-native [ ] expo

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 looked through the other issues with @BeforeInsert and its not firing on the save. I have followed the instructions of the other answers, but I have had no luck.

Creating a new instance of the Entity, Identity

let identity: Identity = new Identity();
    identity.username = 'test';
    identity.password = 'password';
    await getRepository(Identity).save(identity);

Identity

@Entity()
export default class Identity {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  username: string;

  @Column()
  password: string;

  @BeforeInsert()
  async generatePasswordHash(): Promise<void> {
    console.log('GENERATE');
    this.password = await bcrypt.hashSync(this.password, bcrypt.genSaltSync(10));
  }

What am I doing wrong? I have tested both ways with making generatePasswordHash async and not. No luck.

nealdyrkacz commented 4 years ago

I was able to get subscribers to work though.

TweededBadger commented 4 years ago

I'm fairly sure when you call: await getRepository(Identity).save(identity); identity is just getting used an object, so you are coming up against the issue here: https://github.com/typeorm/typeorm/issues/2924 This means that the instance of Identity that is created isn't actually instantiated, and therefore the BeforeInsert won't call.

I think you'll need to run await entityManager.save(identity); instead.

It feels like this should be mentioned in the docs.

nealdyrkacz commented 4 years ago

Hmmmm const entityManager = getManager(); const obj - entityManager.create(Obj) await entityManger.save(obj) hits the @BeforeInsert and works.

while const entityManager = getManager(); const obj - new Obj(); await entityManger.save(obj)

does not

nealdyrkacz commented 4 years ago

@TweededBadger , not sure of the difference.... the documentation says getManager().create() is the same as using new. But thanks for pointing me in the right direction.

iliyaZelenko commented 4 years ago

I had a similar problem. I replaced this code:

this.ClipsRepository.save({
      title,
      start,
      end,
      author,
      description,
      videoId
    })

With:

this.ClipsRepository.save(
      this.ClipsRepository.create({
        title,
        start,
        end,
        author,
        description,
        videoId
      })
    )

And now the @BeforeInsert() is called.

onichandame commented 3 years ago

Any update on this? If it's a bug, how and when are we expecting to fix it? If it's a feature, perhaps we should patch the documentation

faridanthony commented 2 years ago

Agreed. Ran into this on my project as well. A bit annoying having to call .create({...}) before every .save() or .update() to fire beforeInsert

xdc0 commented 2 years ago

Looks like a limitation on the EventListenerMetadata class when checking if the object is eligible to have callbacks applied to it: https://github.com/typeorm/typeorm/blob/1197f881382d5d7a1f604ab53ae34e9461bf1ffe/src/metadata/EntityListenerMetadata.ts#L61

Plain objects will not pass the validation with the current implementation.

jnovak-SM2Dev commented 2 years ago

Yeah any update on this? It's a mess that every bit of documentation on this is wrong. I've even been going through 3 different courses on this and all of them say different ways to update and none of them work lol.

JesperBalslev commented 2 years ago

Seriously, this feels beyond broken, what is the correct way to handle this issue? Is this really the way to do it:

result = await this.save(this.create(object));

Why on earth does .save() not trigger @BeforeInsert?

JoePotentier commented 2 years ago

Any update on this? Just ran into it not encrypting passwords...

hansanwok commented 2 years ago

My case, just pass new Instance of User instead of createUserDto object to insert method

async create(createUserDto: CreateUserDto): Promise<InsertResult> {
    const userInstance: User = this.usersRepository.create(createUserDto);
    return this.usersRepository.insert(userInstance);
  }
ThatAnonyG commented 1 year ago

I was able to get subscribers to work though.

Hey can you tell me how you got that to work? I cannot get Subscribers to work with a similar setup where I am saving objects directly instead of instantiating them.

makhloughi commented 7 months ago

still broken? it's 2024 when they consider a fix for that?

briermay commented 2 months ago

sad part is, imo, this is as simple as going into their save call, and have IT automatically call create.