TypeORM AfterSave() 创建后触发但查询时返回NULL

Wel*_*elp 5 javascript typescript typeorm nestjs

我有一个名为 的实体Trip。结构是: 数据库结构

我想要的是每当创建新旅行时,该room列都应该填充${tripId}_${someRandomStringHere}. 例如,我刚刚使用这个主体创建了一个新的旅行:

旅行主体

响应应该是: tr]ip 响应

新创建的trip具有. 因此,响应的值再次为:。id15room15_4gupvdo0ea408c25ia0qsbh${tripId}_${someRandomStringHere}

每当我请求并创建行程时,这都会按预期工作。但是每当我查询创建的所有行程时,每个行程对象的属性都会显示!POSTroomnull

Look at the /api/trips:

行程数组

room属性为NULL。所以到底发生了什么我不明白发生了什么。

我的Trip Entity代码是:

import { PrimaryGeneratedColumn, Column, CreateDateColumn, 
UpdateDateColumn, Entity, Unique, ManyToOne, AfterInsert, JoinColumn, getConnection } from 'typeorm'
import { DriverEntity } from 'src/driver/driver.entity';

@Entity('trips')
export class TripEntity {
  @PrimaryGeneratedColumn()
  id: number

  @Column()
  destination: string

  @Column('decimal')
  destination_lat: number

  @Column('decimal')
  destination_long: number

  @Column()
  maxPassenger: number

  @Column()
  totalPassenger: number

  @Column({ nullable: true })
  room: string

  @CreateDateColumn()
  created_at: Date

  @UpdateDateColumn()
  updated_at: Date
// --------->HERE: The after insert 
  @AfterInsert()
  async createSocketRoom(): Promise<void> {
    const randomString = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
    this.room =  `${this.id}_${randomString}`
  }

  // Trip belongs to driver
  // Adds driver_id to trips table
  @ManyToOne(type => DriverEntity, driver => driver.trips)
  @JoinColumn({ name: 'driver_id' })
  driver: DriverEntity
}
Run Code Online (Sandbox Code Playgroud)

我的Trip Service Code是:

async create(data: CreateTripDTO) {
    const { driver_id } = data
    const driver = await this.driverRepository.findOne({ where: { id: driver_id } })
    const trip = await this.tripRepository.create(data)
    trip.driver = driver

    await this.tripRepository.save(trip)
    return trip
  }
Run Code Online (Sandbox Code Playgroud)

我不认为我需要包含代码Trip Controller,但无论如何..我不知道为什么会发生这种情况,因为我的用户实体带有 @BeforeUpdate 并且工作正常...

在阅读了很多类似的 github 问题后,观看了 youtube 教程 [Hi Ben Awad!:D],我找到了一个修复方法..通过使用订阅者

其实我不知道Listener/Subscriber有什么区别。也许我的用法是错误的。有人可以启发我吗?例如实体监听器的 AfterSave 与实体订阅者的 AfterSave 的区别。何时/最佳情况下使用?类似的事情。无论如何回来"fix..."

我创建了一个Trip Subscriber

import { EventSubscriber, EntitySubscriberInterface, InsertEvent } from "typeorm";
import { TripEntity } from "src/trip/trip.entity";

@EventSubscriber()
export class TripSubsriber implements EntitySubscriberInterface<TripEntity> {
    // Denotes that this subscriber only listens to Trip Entity
    listenTo() {
      return TripEntity
    }

    // Called after entity insertion
    async afterInsert(event: InsertEvent<any>) {
        console.log(`AFTER ENTITY INSERTED: `, event.entity);
        const randomString = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
        // query the trip with given event.entity
        const trip = await event.manager.getRepository(TripEntity).findOne(event.entity.id)
        // populate the room with desired format
        trip.room = `${trip.id}_${randomString}`
        // save it!
        await event.manager.getRepository(TripEntity).save(trip)
    }

}
Run Code Online (Sandbox Code Playgroud)

起初它不起作用,但在再次挖掘了几个小时后,我需要添加一个subscriber具有订阅者路径值的属性才能使其ormconfig.json工作!例如:"subscribers": [ "src/subscriber/*.ts" ]

同样,Trip Subscriber代码对我来说就像意大利面条,因为我已经拥有该event.entity对象,但我不知道如何更新它,而不需要使用event.manager.getRepository(). 请问有人可以帮我修复这个代码吗?正确的做法是什么?

现在,它正在工作! 请求正文: 请求正文

/api/trips 资源: 资源

我的问题是:

  1. 为什么每当我使用该方法方法订阅者时,它都不起作用。这不是正确的做法吗?为什么它在文档中?或者用于其他用例?
  2. 我真的必须使用订阅者才能实现吗?就这么多步骤。

我来自 Rails。因此,必须创建文件/订阅者才能做到这一点有点累。与 ActiveRecord 的after_save回调不同,它非常简单。

附言。我是 Nest-js 和 typeorm 的新手

小智 6

@AfterInsert方法将在插入数据库完成后修改您的 JS 对象。这就是您的代码无法正常工作的原因。你必须使用@BeforeInsert装饰器。BeforeInsert将在插入/保存到数据库之前修改您的 JS 实体/对象。

  • 我认为文档应该根据每个差异进行更新...AfterInsert 在侦听器与订阅者中。何时使用每个等等 (2认同)

Jay*_*iel 3

您的情况似乎是AfterInsert,您正在创建随机房间字符串,但您没有将该值保存到数据库中,仅使用 id 的返回值以便您可以创建该字符串。您可以在您中做的AfterInsert是再次运行该save()函数EntityManager并将RepositoryManger值提交到数据库,类似于您在您中发生的情况Subscriber。我还没有深入研究Subscriber/ ListenervsBefore-/AfterInsert装饰器,所以我无法对你的问题给出更深入的答案。

如果您不想对数据库进行两次提交,则始终可以查询最近的 id 并将其增加 1(因此,匹配新对象 id应该是什么),类似于

const maxId = await this.tripRepository.findOne({select: ['id'], order: {id: "DESC"} });
const trip = await this.tripRepository.create(data);
const randomString = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
this.room =  `${maxId + 1}_${randomString}`
trip.driver = driver
await this.tripRepository.save(trip)
Run Code Online (Sandbox Code Playgroud)

它看起来有点笨重,但它不需要对数据库进行两次写入(尽管您肯定需要确保创建后 room 和 trip 具有相同的 id)。

最后一个选择是在数据库中创建一个触发器,它与 JavaScript 代码执行相同的操作。