TypeORM 数据库锁:请解释如何使用 setLock() 函数在 TypeORM 中使用数据库锁

Jos*_*eph 5 postgresql node.js typescript typeorm nestjs

我想使用 Nestjs 和 TypeORM 在 PostgreSQL 中实现数据库行锁定。请任何人告诉我如何以正确的方式实施它。我尝试使用以下代码,但不确定它是否锁定特定行。

async testFun(): Promise<any> {
        const user = await this.userRepo
                    .createQueryBuilder("user")
                    .useTransaction(true)
                    .setLock("pessimistic_write")
                    .where("id = :id", { id: 2 })
                    .getOne()

        const updateResult: any = await this.userRepo.createQueryBuilder('test')
                                .update()
                                .set({ fullname: "frm3000" })
                                .where("id = :id", { id: 2 })
                                .execute();
        
        return updateResult;
    }
Run Code Online (Sandbox Code Playgroud)

TypeORM 生成的查询是,

query: START TRANSACTION
query: SELECT * FROM "users" "user" WHERE id = $1 FOR UPDATE -- PARAMETERS: [2]      
query: COMMIT
query: UPDATE "users" SET "fullname" = $1 WHERE "id" = $2 -- PARAMETERS: ["from3000",2]
Run Code Online (Sandbox Code Playgroud)

这里提交是在更新查询之前完成的,因此怀疑该行是否被锁定。

另外,我想在本地主机上测试它,使用两个不同的端口 localhost:3000 和 localhost:3001。因此,我在另一个具有延迟端口 3001 的应用程序中添加了相同的控制器和服务代码,

async testFun(): Promise<any> {
        const user = await this.userRepo
                    .createQueryBuilder("user")
                    .useTransaction(true)
                    .setLock("pessimistic_write")
                    .where("id = :id", { id: 2 })
                    .getOne()
        

        function sleep(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }
        await sleep(20000);

        const updateResult: any = await this.userRepo.createQueryBuilder('test')
                                .update()
                                .set({ fullname: "frm3001" })
                                .where("id = :id", { id: 2 })
                                .execute();
        
        return updateResult;
    }
Run Code Online (Sandbox Code Playgroud)

请让我知道这种方法是否正确或建议更好的方法(如果有)。而且测试方法也不起作用。

谢谢!

Moh*_*eem 7

由于您正在运行query而没有启动transactionon TypeORM. 所有的个体queries都被视为一个单独的transaction个体database

queries要在 a 中运行一组,single transaction您必须Demarcate transaction在应用程序级别运行。transaction设置分界的方法之一TypeORM是使用QueryRunner

执行以下步骤:

const queryRunner = getConnection().createQueryRunner();
  
try {
    await queryRunner.connect();
    await queryRunner.startTransaction();
    const user = await queryRunner.manager.getRepository(User)
                    .createQueryBuilder("user")
                    .useTransaction(true)
                    .setLock("pessimistic_write")
                    .where("id = :id", { id: 2 })
                    .getOne()
    function sleep(ms) {
         return new Promise(resolve => setTimeout(resolve, ms));
    }
    await sleep(20000);

    const updateResult: any = await queryRunner.manager.getRepository(User)
                                .createQueryBuilder('test')
                                .update()
                                .set({ fullname: "frm3001" })
                                .where("id = :id", { id: 2 })
                                .execute();
    await queryRunner.commitTransaction();    
    return updateResult;
} catch(e) {
    await queryRunner.rollbackTransaction();
} finally {
    await queryRunner.release();
}
Run Code Online (Sandbox Code Playgroud)

注意:永远不要忘记释放queryRunner,否则你的数据库连接将被用完。

希望这可以帮助。