TypeORM + Typescript の実装サンプル

TypeORM + Typescript の実装サンプル

〇サンプルに使用するテーブル

CREATE TABLE shopping_items (
    id bigint IDENTITY(1,1) NOT NULL,
    name varchar(40) COLLATE Japanese_CI_AS NOT NULL,
    price int NOT NULL,
    suryo int NULL,
    description varchar(100) COLLATE Japanese_CI_AS NULL,
    version int NOT NULL,
    CONSTRAINT shopping_items_pk PRIMARY KEY (id)
);

サンプルプロジェクト作成

TypeORM

--nameにプロジェクト名、--databaseには使用するデータベース名を記述する。

npx typeorm init --name typeorm_sample --database mssql

Entity作成

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity({name:"shopping_items"})
export class ShoppingItem {

    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string

    @Column()
    price: number

    @Column()
    suryo: number

    @Column()
    description: string

    @VersionColumn()
    version: number
}

Entityの自動生成

別途ツールを使用することで、Entityを自動生成することが出来る。
typeorm-model-generator

〇ツールのインストール

npm i typeorm-model-generator

〇ツールの実行

npx typeorm-model-generator -h <host> -d <databaseName> -p [port] -u <user> -x [password] -e [engine] -o [output]

自動生成で作成されたEntityのサンプルは以下の通り。

import { Column, Entity, Index, PrimaryGeneratedColumn } from "typeorm";

@Index("shopping_items_pk", ["id"], { unique: true })
@Entity("shopping_items", { schema: "dbo" })
export class ShoppingItems {
  @PrimaryGeneratedColumn({ type: "bigint", name: "id" })
  id: string;

  @Column("varchar", { name: "name", length: 40 })
  name: string;

  @Column("int", { name: "price" })
  price: number;

  @Column("int", { name: "suryo", nullable: true })
  suryo: number | null;

  @Column("varchar", { name: "description", nullable: true, length: 100 })
  description: string | null;

  @Column("int", { name: "version" })
  version: number;
}

接続先の設定

data-source.tsにDBの接続先情報を定義する。 importおよびentitiesに作成したEntityを追加する。

import "reflect-metadata"
import { DataSource } from "typeorm"
import { ShoppingItem } from "./entity/ShoppingItem"

// https://typeorm.io/data-source-options#mssql-data-source-options
export const AppDataSource = new DataSource({
    type: "mssql",
    host: "localhost",
    username: "aa00001",
    password: "Xaa00001",
    database: "Test",
    synchronize: false,
    logging: true,
    entities: [ShoppingItem],
    migrations: [],
    subscribers: [],
    // コネクションプールの設定
    pool: {
        max: 10,
        min: 0
    }
})

DB操作処理記述

index.tsにDB操作処理を記述する。

import { AppDataSource } from "./data-source"
import { ShoppingItem } from "./entity/ShoppingItem"

AppDataSource.initialize().then(async () => {
    // ◇ insertサンプル
    const shoppingItem = new ShoppingItem();
    shoppingItem.name = "おやつ";
    shoppingItem.price = 200;
    shoppingItem.suryo = 3;
    shoppingItem.description = "バナナもOK";
    await AppDataSource.manager.save(shoppingItem);
    
    // saveメソッドに複数のEntityを渡すことで
    // 1回のクエリ発行で複数行のinsertを実行できる
    const shoppingItem2 = new ShoppingItem();
    shoppingItem2.name = "ピザ";
    shoppingItem2.price = 2000;
    shoppingItem2.suryo = 3;
    shoppingItem2.description = "サラミ";
    // ◇ 複数行insertサンプル
    await AppDataSource.manager.save([shoppingItem,shoppingItem2]);
    
    // ◇ selectサンプル
     const item = await AppDataSource.manager.findOne(ShoppingItem, {
         where:{
            name: 'ラムネ'
        }
     });
    
    // ◇ updateサンプル
    await AppDataSource.manager.update(ShoppingItem, 15, {name: "チャーハン"});
    
    item.price = 5000;
    await AppDataSource.manager.save(item);
    
    // ◇ deleteサンプル
    await AppDataSource.manager.delete(ShoppingItem, 15);
    
    // removeメソッドに複数のEntityを渡した場合は1回のクエリ発行で複数行deleteされる
    await AppDataSource.manager.remove(item);
    
    // ◇ クエリを書いて実行サンプル
    const rawData = await AppDataSource.manager.query(`SELECT * FROM shopping_items WHERE price = @0`, [100]);
    
    // ◇ QueryBuilderサンプル
    // https://typeorm.io/select-query-builder
    const aa = await AppDataSource
        .manager
        .createQueryBuilder(ShoppingItem, "shoppingItem")
        .where("shoppingItem.price = :price", { price: 100 })
        .getMany();

}).catch(error => console.log(error));

以下のコマンドで実行

npm start

トランザクション処理

import { AppDataSource } from "./data-source"
import { ShoppingItem } from "./entity/ShoppingItem"

AppDataSource.initialize().then(async () => {

    const shoppingItem = new ShoppingItem();
    shoppingItem.name = "グラタン"
    shoppingItem.price = 200
    shoppingItem.suryo = 3
    shoppingItem.description = "マカロニ"
    shoppingItem.version = 1

    // コールバック関数の中で実行したSQLで例外が発生した場合はロールバックされる
    // 最後まで処理が実行された場合はコミットされる
    await AppDataSource.manager.transaction(async (transactionalEntityManager) => {
        await transactionalEntityManager.save(shoppingItem);
    })

}).catch(error => console.log(error))

登録/更新時の登録/更新日時の登録

エンティティに@BeforeInsert()もしくは@BeforeUpdate()アノテーションを付与した関数を定義する。
他にも使用可能なアノテーションが存在しているので以下の公式のページ参照の事。
https://typeorm.io/listeners-and-subscribers#what-is-an-entity-listener

@BeforeInsert():登録(save())前に行いたい処理を記述する
@BeforeUpdate():更新(save())前に行いたい処理を記述する

import { BeforeInsert, BeforeUpdate, Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";

@Index("PK_goods", ["id"], { unique: true })
@Entity("goods", { schema: "dbo" })
export class Goods {
    @PrimaryGeneratedColumn({ type: "int", name: "id" })
    id: number;

    @Column("nvarchar", { name: "name", nullable: true, length: 20 })
    name: string | null;

    @Column("decimal", { name: "price", nullable: true, precision: 9, scale: 0 })
    price: number | null;

    @Column("datetime2", { name: "create_date", nullable: true })
    createDate: Date | null;

    @Column("varchar", { name: "create_id", nullable: true, length: 128 })
    createId: string | null;

    @Column("datetime2", { name: "update_date", nullable: true })
    updateDate: Date | null;

    @Column("varchar", { name: "update_id", nullable: true, length: 128 })
    updateId: string | null;

    // 登録前に登録日時・登録者IDを設定する
    @BeforeInsert()
    createDates() {
        this.createDate = new Date();
        this.createId = "A0001";
    }

    // 更新前に更新日時・更新者IDを設定する
    @BeforeUpdate()
    updateDates() {
        this.updateDate = new Date();
        this.updateId = "A0001";
    }
}