前回に続いてtypeorm記事です。
@VersionColumn()と、setLock("optimistic", version)の動作確認をします。
排他について
調べれば出てきますが参考になったものをあげておきます。
こちらの資料はとても丁寧に記載されている。
https://terasolunaorg.github.io/guideline/5.3.0.RELEASE/ja/ArchitectureInDetail/DataAccessDetail/ExclusionControl.html
上記が長く、概要だけ理解したければ以下
https://mintaku-blog.net/exclusion-control/
結果
バージョンのインクリメントが行われ、バージョンに違いがあればエラーとなることを確認
コード
entity
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany((type) => Question, (question) => question.categories)
questions: Promise<Question[]>;
}
@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
text: string;
@ManyToMany((type) => Category, (category) => category.questions)
@JoinTable()
categories: Promise<Category[]>;
@VersionColumn()
version: number;
}
controller
@Controller("test")
export class QuestionController {
constructor(private dataSource: DataSource) {}
@Post("question")
async create() {
const category1 = new Category();
category1.name = "animals";
await this.dataSource.manager.save(category1);
const category2 = new Category();
category2.name = "zoo";
await this.dataSource.manager.save(category2);
const question = new Question();
question.categories = Promise.resolve([category1, category2]);
question.title = "aaa";
question.text = "bbb";
await this.dataSource.manager.save(question);
const [questions] = await this.dataSource.getRepository(Question).find();
const categories = await question.categories;
console.log([questions]);
}
@Post("question2")
async update() {
//上で登録したデータを画面に表示していると想定。
const version = 1;
const id = 1;
const users = await this.dataSource
.getRepository(Question)
.createQueryBuilder("Question")
.setLock("optimistic", version)
.getMany();
await this.dataSource
.createQueryBuilder()
.update(Question)
.set({ title: "Timber" })
.where("id = :id", { id: id })
.execute();
const question = new Question();
const [questions] = await this.dataSource.getRepository(Question).find();
const categories = await question.categories;
console.log([questions]);
}
}
実行ログ
/test/question
query: START TRANSACTION
query: INSERT INTO "category"("name") VALUES ($1) RETURNING "id" -- PARAMETERS: ["animals"]
query: COMMIT
query: START TRANSACTION
query: INSERT INTO "category"("name") VALUES ($1) RETURNING "id" -- PARAMETERS: ["zoo"]
query: COMMIT
query: START TRANSACTION
query: INSERT INTO "question"("title", "text", "version") VALUES ($1, $2, 1) RETURNING "id", "version" -- PARAMETERS: ["aaa","bbb"]
query: INSERT INTO "question_categories_category"("questionId", "categoryId") VALUES ($1, $2), ($3, $4) -- PARAMETERS: [1,1,1,2]
query: COMMIT
query: SELECT "Question"."id" AS "Question_id", "Question"."title" AS "Question_title", "Question"."text" AS "Question_text", "Question"."version" AS "Question_version" FROM "question" "Question"
[ Question { id: 1, title: 'aaa', text: 'bbb', version: 1 } ]
test/question2
1回目(成功)
query: SELECT "Question"."id" AS "Question_id", "Question"."title" AS "Question_title", "Question"."text" AS "Question_text", "Question"."version" AS "Question_version" FROM "question" "Question"
query: UPDATE "question" SET "title" = $1, "version" = "version" + 1 WHERE "id" = $2 -- PARAMETERS: ["Timber",1]
query: SELECT "Question"."id" AS "Question_id", "Question"."title" AS "Question_title", "Question"."text" AS "Question_text", "Question"."version" AS "Question_version" FROM "question" "Question"
query: SELECT "categories"."id" AS "categories_id", "categories"."name" AS "categories_name" FROM "category" "categories" INNER JOIN "question_categories_category" "question_categories_category" ON "question_categories_category"."questionId" IN ($1) AND "question_categories_category"."categoryId"="categories"."id" -- PARAMETERS: [null]
[ Question { id: 1, title: 'Timber', text: 'bbb', version: 2 } ]
2回目(失敗)
query: SELECT "Question"."id" AS "Question_id", "Question"."title" AS "Question_title", "Question"."text" AS "Question_text", "Question"."version" AS "Question_version" FROM "question" "Question"
[Nest] 18929 - 2023/07/25 20:24:27 ERROR [ExceptionsHandler] The optimistic lock on entity Question failed, version 1 was expected, but is actually 2.
OptimisticLockVersionMismatchError: The optimistic lock on entity Question failed, version 1 was expected, but is actually 2.