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) );
サンプルプロジェクト作成
--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"; } }
Sequelizeの実装サンプルは以下の記事参照
Prismaの実装サンプルは以下の記事参照