LoginSignup
2
1

typeormで楽観排他やってみた。

Posted at

前回に続いて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.
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1