带有@NestJs/mongoose的MongoDB事务不工作

u91tlkcl  于 2023-01-25  发布在  Go
关注(0)|答案(1)|浏览(263)

我真的需要你的帮助。我的MongoDB交易与@NestJs/mongoose不工作...当我的条纹支付失败回滚不工作...尽管如此,我的订单收集保存了数据...我该如何解决这个问题...?

async create(orderData: CreateOrderServiceDto): Promise<any> {
    const session = await this.connection.startSession();
    session.startTransaction();
    try {
      const createOrder = new this.orderModel(orderData);
      const order = await createOrder.save();

      await this.stripeService.charge(
        orderData.amount,
        orderData.paymentMethodId,
        orderData.stripeCustomerId,
      );
      await session.commitTransaction();
      return order;
    } catch (error) {
      await session.abortTransaction();
      throw error;
    } finally {
      await session.endSession();
    }
  }
2hh7jdfx

2hh7jdfx1#

我也有同样的问题,我发现在github:Mongo DB Transactions With Mongoose & Nestjs
所以我认为,根据这个问题,你必须调用你的模型的create方法,就像这样:

const order = await this.orderModel.create(orderData, { session });

正如您所看到的,Model.create方法有一个重载,它以SaveOptions作为参数:

create(docs: (AnyKeys<T> | AnyObject)[], options?: SaveOptions): Promise<HydratedDocument<T, TMethodsAndOverrides, TVirtuals>[]>;

它采用可选的SaveOptions参数,该参数可以包含会话:

interface SaveOptions {
  checkKeys?: boolean;
  j?: boolean;
  safe?: boolean | WriteConcern;
  session?: ClientSession | null;
  timestamps?: boolean;
  validateBeforeSave?: boolean;
  validateModifiedOnly?: boolean;
  w?: number | string;
  wtimeout?: number;
}

请注意,Model.save()也可以带SaveOptions参数,所以您也可以像这样做:

const createOrder = new this.orderModel(orderData);
const order = await createOrder.save({ session });

再往前一点...
由于我做的许多事情都需要事务,所以我提出了这个帮助器来避免许多代码重复:

import { InternalServerErrorException } from "@nestjs/common"
import { Connection, ClientSession } from "mongoose"

export const mongooseTransactionHandler = async <T = any>(
  method: (session: ClientSession) => Promise<T>,
  onError: (error: any) => any,
  connection: Connection, session?: ClientSession
): Promise<T> => {
  const isSessionFurnished = session === undefined ? false : true
  if (isSessionFurnished === false) {
    session = await connection.startSession()
    session.startTransaction()
  }

  let error
  let result: T
  try {
    result = await method(session)

    if (isSessionFurnished === false) {
      await session.commitTransaction()
    }
  } catch (err) {
    error = err
    if (isSessionFurnished === false) {
      await session.abortTransaction()
    }
  } finally {
    if (isSessionFurnished === false) {
      await session.endSession()
    }

    if (error) {
      onError(error)
    }

    return result
  }
}

详细信息

可选参数session是在你正在做嵌套事务的情况下。这就是为什么我检查会话是否被提供。如果是,这意味着我们在一个嵌套事务中。所以我们将让主事务提交,中止和结束会话。

示例

例如:您删除了一个User模型,然后删除了用户的头像(File模型)。

/** UserService **/
async deleteById(id: string): Promise<void> {
  const transactionHandlerMethod = async (session: ClientSession): Promise<void> => {
    const user = await this.userModel.findOneAndDelete(id, { session })
    await this.fileService.deleteById(user.avatar._id.toString(), session)
  }

  const onError = (error: any) => {
    throw error
  }

  await mongooseTransactionHandler<void>(
    transactionHandlerMethod,
    onError,
    this.connection
  )
}

/** FileService **/
async deleteById(id: string, session?: ClientSession): Promise<void> {
  const transactionHandlerMethod = async (session: ClientSession): Promise<void> => {
    await this.fileModel.findOneAndRemove(id, { session })
  }

  const onError = (error: any) => {
    throw error
  }

  await mongooseTransactionHandler<void>(
    transactionHandlerMethod,
    onError,
    this.connection,
    session
  )
}

因此,简而言之:

您可以像这样使用它:

async create(orderData: CreateOrderServiceDto): Promise<any> {
  const transactionHandlerMethod = async (session: ClientSession): Promise<Order> => {
    const createOrder = new this.orderModel(orderData);
    const order = await createOrder.save({ session });

    await this.stripeService.charge(
      orderData.amount,
      orderData.paymentMethodId,
      orderData.stripeCustomerId,
    );

    return order
  }

  const onError = (error: any): void => {
    throw error
  }

  const order = await mongooseTransactionHandler<Order>(
    transactionHandlerMethod,
    onError,
    this.connection
  )

  return order
}

希望能有所帮助。

编辑

不要在嵌套事务中滥用相同模型的model.save({ session })。由于某些原因,模型更新太多次会引发错误。
为了避免这种情况,最好使用模型嵌入方法,这些方法更新并返回模型的新示例(例如model.findOneAndUpdate)。

相关问题