JavaScriptは、Web用に設計されたECMAScript仕様に基づくスクリプト言語です。JavaScriptは、クライアントサイドのみのスクリプト言語からクライアントサイドとサーバサイドの両方で動作するものへと進化しました。サーバサイドJavaScriptの最も注目すべき実装はNode.jsです。
問題
JavaScriptには、現代の大規模Webアプリケーションで使用される機能がいくつか欠けています。 型アノテーション、コンパイル時の型チェック、型推論などの機能です。JavaScriptコードは大規模アプリケーションでは複雑になります。
解決策
TypeScriptは、大規模アプリケーションにおけるJavaScriptの制限を緩和するために、JavaScriptの型付けされたスーパーセットとして導入されました。
TypeScriptは、JavaScriptの厳密な構文上のスーパーセットで、コンパイル時の型チェック、型アノテーション、型推論、型消去などの機能と、インターフェイスのサポートなどのオブジェクト指向の機能を追加したものです。TypeScriptはオープンソースのスクリプト言語であり、JavaScriptに変換されます。出力された出力は、マシンだけが読み取ることができるものではなく、クリーンで慣用的なJSです。
TypeScriptは、アノテーションとメタプログラミング構文を使ってクラスとクラスメンバに追加機能を追加するためのサポートを提供するDecoratorsと呼ばれる実験的な機能を追加しました。デコレータは@expression
形式の宣言で、expression
は実行時に呼び出される関数として評価され、デコレートされた宣言に関する情報も含まれます。デコレータは、クラス宣言、メソッド、アクセッサ、プロパティ、アノテーションに注釈を付けるために使用されます。この記事ではデコレータが使用されます。
2012年に導入されたTypeScriptは最近人気が急上昇しています。最近のJavaScriptとWeb開発のInfoQのトレンドレポートには、「TypeScriptが劇的に増加し、GitHubで最も人気のあるプログラミング言語のトップ10に入っている」ことが記されています。2018年6月、TypeScriptは93位でプログラミング言語のTIOBEインデックスのトップ100でデビューし、翌月はトップ50にランクインしました。最近では、TypeScriptはTIOBEインデックスの44位にランクインしています。
TypeScriptはWebアプリケーションを作成するための強力な環境として浮上してきました。言語との整合性を保ちながら、標準のJavaScriptよりも大幅に改善されています。この記事では、Node.js、MySQL、TypeORMでTypeScriptを使用してサーバサイドTypeScriptでデータベースアクセスを管理するための強力なソリューションを作成するために必要な詳細について詳しく説明します。完全なエンドツーエンドのソリューションを提供するために、CRUDアプリケーションの例を作成します。 サンプルアプリケーションはジャーナルカタログをモデル化するものとします。JavaScriptをよく知っていることを前提としています。この記事には以下のセクションがあります。
- 環境の設定
- プロジェクトの作成
- プロジェクトの設定
- エンティティの作成
- テーブルを生成するためのコネクションの作成
- アプリケーションの実行
- データベーステーブルの探索
- リポジトリを使用したデータの追加
- Connection Managerを使用したエンティティの検索/li>
- リポジトリを使用したエンティティの検索
- データの更新
- データの削除
- 1対1のリレーションの作成
- リレーションを持つオブジェクトの検索
- 1対多リレーションの作成
- 多対多リレーションの作成
環境の設定
次のソフトウェアをダウンロードしてインストールします。
次に、Node.jsモジュール(パッケージ)をいくつかインストールする必要があります。TypeORM パッケージを使用します。これはMySQLデータベースを含むほとんどのリレーショナルデータベースにアクセスするためのTypeScriptのオブジェクトリレーショナルマッピングを提供します。
typeorm
パッケージをインストールしてください。
npm install typeorm -g
Install the reflect-metadata
library, which is needed when using class decorators. The reflect-metadata
library is experimental just as decorators are.
クラスデコレータを使用する場合に必要なreflect-metadata
ライブラリをインストールします。reflect-metadata
ライブラリはデコレータと同じように実験的なものです。
npm install reflect-metadata -g
ノードタイピングをインストールします。
npm install @types/node -g
MySQLデータベースドライバをインストールします。
npm install mysql -g
プロジェクトの作成
MySQLデータベース用のTypeORMプロジェクトを作成します。プロジェクト名は任意です。(MySQLProject
)。
typeorm init --name MySQLProject --database mysql
プロジェクトディレクトリMySQLProject
が作成されます。ディレクトリ(cd
)をMySQLProject
ディレクトリに変更し、プロジェクトファイルを一覧表示します。
cd MySQLProject
C:\Typescript\MySQLProject>DIR
Volume in drive C is OS
Volume Serial Number is BEFC-C04A
Directory of C:\Typescript\MySQLProject
02/02/2019 05:17 PM <DIR> .
02/02/2019 05:17 PM <DIR> ..
02/02/2019 05:13 PM 47 .gitignore
02/02/2019 05:17 PM <DIR> node_modules
02/02/2019 05:13 PM 473 ormconfig.json
02/02/2019 05:17 PM 46,178 package-lock.json
02/02/2019 05:17 PM 406 package.json
02/02/2019 05:13 PM 172 README.md
02/02/2019 05:13 PM <DIR> src
02/02/2019 05:13 PM 298 tsconfig.json
6 File(s) 47,574 bytes
4 Dir(s) 25,897,005,056 bytes free
C:\Typescript\MySQLProject>
プロジェクトの依存関係をインストールします。
npm install
上記のコマンドの出力が一覧表示されます。
C:\Typescript>typeorm init --name MySQLProject --database mysql
Project created inside C:\Typescript/MySQLProject directory.
C:\Typescript>cd MySQLProject
C:\Typescript\MySQLProject>npm install
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN [email protected] No repository field.
npm WARN [email protected] No license field.
added 153 packages from 447 contributors and audited 231 packages in 70.022s
found 0 vulnerabilities
MySQLProjectの設定ファイルとsrcファイルは、GitHubで入手できます。この記事で追加したスクリプトも含まれています。
プロジェクトの設定
TypeScript コンパイラオプションは、tsconfig.json
で設定されています。 tsconfig.json
を変更して、次の設定を有効にします。
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
ターゲットコンパイルバージョンなど、その他のコンパイラオプションはtsconfig.jsonに事前定義されています。--experimentalDecorators
オプションを使用すると、JavaScript向けのECMAScript(ES)仕様標準に記載されているようにデコレータを実験的にサポートできます。--emitDecoratorMetadata
設定は、ソース内のデコレートされた宣言向けのデザインタイプメタデータを生成します。デコレータメタデータを生成するには、reflect-metadata
ライブラリをTypeScriptプログラムにインポートする必要があります。
MySQLデータベースに接続するためにormconfig.json
を変更して、データベースオプションの設定を変更してください。ローカルのMySQLデータベースが使用されている場合、host
とport
がデフォルトになりますが、username
とpassword
は違います。
{
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "mysql",
"database": "mysql",
"synchronize": true,
"logging": false,
"entities": [
"src/entity/**/*.ts"
],
"migrations": [
"src/migration/**/*.ts"
],
"subscribers": [
"src/subscriber/**/*.ts"
]
}
エンティティの作成
このセクションでは、ジャーナルカタログをモデル化するためのエンティティを作成します。 エンティティは、データベースのテーブルにマップするクラスです。クラスをエンティティにするのは、typeormライブラリのデコレータ@Entity()
です。entity
ディレクトリにCatalog.ts
ファイルを追加してエンティティを定義します。 typeorm
ライブラリからEntity, Column,
およびPrimaryGeneratedColumn
関数をインポートするためのimport
宣言を追加します。
import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
Catalog
というコードをエクスポートし、そのクラスを@Entity()でアノテーションまたはデコレートします。
@Entity()
export class Catalog {
…
}
完全なCatalog.tsスクリプトはGitHubから入手できます。基本エンティティは列で構成されており、すべてのエンティティは少なくとも1つのプライマリ列を宣言する必要があります。TypeORMは、表1で説明したように、いくつかの種類のプライマリ列を提供します。
プライマリ列種別 |
説明 |
@PrimaryColumn() |
プライマリ列を作成する。列の種類はオプションで指定でき、設定されていない場合はプロパティタイプから推測される。プライマリ列の値はユーザ指定でなければならない。 |
@PrimaryGeneratedColumn() |
自動インクリメント値で自動的に生成されるint型のプライマリカラムを作成する。 |
@PrimaryGeneratedColumn("uuid") |
uuid値で自動的に生成されるプライマリカラムを作成する。 uuidは一意の文字列値である。 |
Table 1. プライマリ列種別
Catalogエンティティにid
という名前のプライマリ列を追加します。MySQLデータベースでは、新しいデータ行ごとにデータベースによって一意の主キー値が自動的に割り当てられるプライマリ列の自動インクリメントがサポートされています。自動インクリメント機能を利用するには、自動生成された列@PrimaryGeneratedColumn
を使用してください。
@PrimaryGeneratedColumn()
id: number;
サンプルアプリケーションでジャーナルカタログをモデル化しています。他の列を追加してください。journal, publisher, edition, title
およびauthor
ですべてstring
型です。カタログエントリが公開されているかどうかを示すisPublished
というタイプのBoolean
を追加します。エンティティプロパティタイプは適切なデータベース列タイプにマッピングされます。これは使用するデータベースによって異なります。string
型はvarchar (255)
または類似のデータベースタイプにマッピングされます。 number
プロパティタイプは、integer
または同様のデータベースタイプにマッピングされます。 entity-データベースタイプのマッピングもユーザが指定できます。例として、string
型の列title
をMySQLデータベースタイプのtextにマッピングします。
@Column("text")
title: string;
string
型のデフォルトの長さは255ですが、例にあるようにカスタムの長さを指定できます。
@Column({
length: 25
})
edition: string;
次のリストをエンティティファイルにコピーして貼り付けます。
import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
@Entity()
export class Catalog {
@PrimaryGeneratedColumn()
id: number;
@Column()
journal: string;
@Column()
publisher: string;
@Column({
length: 25
})
edition: string;
@Column("text")
title: string;
@Column()
author: string;
@Column()
isPublished: boolean;
}
テーブルを生成するためのコネクションの作成
前述のとおり、CRUD(作成、読み取り、更新、削除)アプリケーションを開発しています。アプリケーションを開発できるようにするには、MySQLデータベースに接続する必要があります。
このセクションでは、MySQLデータベースに接続し、entity
ディレクトリに定義されたエンティティのテーブルを作成するためのTypeScriptスクリプトを開発します。entity/Catalog.ts
ファイルに定義したエンティティは1つだけです。プロジェクトのデフォルトのUser.ts
を含む他のentity
スクリプトをエンティティディレクトリから削除します。TypeScriptプロジェクトのMySQLProject
のsrc
ディレクトリにindex.js
を作成します。デコレータを使用できるようにするには、reflect-metadata
ライブラリをインポートします。typeorm
ライブラリからcreateConnection関数をインポートします。entity
ディレクトリからCatalog
クラスをインポートします。ES2017で追加されたasync/await構文を使用します。async/await
構文では、非同期関数の前にasync
キーワードが付きます。await
キーワードは、非同期関数のreturnが約束されるまでスクリプトの実行を中断します。createConnection
関数を使用して接続を作成します。この場合、データベースのtype, host, port, username, password, database
の名前、entities
などの接続オプションが指定されます。
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "mysql",
database: "mysql",
entities: [
__dirname + "/entity/*.ts"
],
synchronize: true,
logging: false
}).then(async connection => {
…
…
}).catch(error => console.log(error));
then
ブロックで、Catalog
エンティティのインスタンスを作成します。
let catalog = new Catalog();
カタログエントリを作成するために、エンティティプロパティの値を設定します。
catalog.journal = "Oracle Magazine";
catalog.publisher = "Oracle Publishing";
catalog.edition = "March-April 2005";
catalog.title = "Starting with Oracle ADF";
catalog.author = "Steve Muench";
catalog.isPublished = true;
EntityManager
のインスタンスを取得し、save
メソッドを使用してエンティティインスタンスを保存します。
await connection.manager.save(catalog);
save
メソッドは、指定されたすべてのエンティティをデータベースに保存します。 save
メソッドは、まずエンティティがすでにデータベースに存在するかどうかを確認します。存在すればsave
メソッドによりエンティティが更新され、そうでない場合、saveメソッドは新しいエンティティを追加します。 同様に、別のエンティティを追加してください。src/index.ts
を一覧表示します。
import "reflect-metadata";
import {createConnection} from "typeorm";
import {Catalog} from "./entity/Catalog";
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "mysql",
database: "mysql",
entities: [
__dirname + "/entity/*.ts"
],
synchronize: true,
logging: false
}).then(async connection => {
let catalog = new Catalog();
catalog.journal = "Oracle Magazine";
catalog.publisher = "Oracle Publishing";
catalog.edition = "March-April 2005";
catalog.title = "Starting with Oracle ADF";
catalog.author = "Steve Muench";
catalog.isPublished = true;
await connection.manager.save(catalog);
console.log('Catalog has been saved'+'\n');
let catalog2 = new Catalog();
catalog2.journal = "Oracle Magazine";
catalog2.publisher = "Oracle Publishing";
catalog2.edition = "November December 2013";
catalog2.title = "Engineering as a Service";
catalog2.author = "David A. Kelly";
catalog2.isPublished = true;
await connection.manager.save(catalog2);
console.log('Catalog has been saved'+'\n');
}).catch(error => console.log(error));
アプリケーションの実行
アプリケーションを作成したら、次のコマンドでアプリケーションを実行します。
npm start
index.js
を実行してデータベースに接続すると、プロジェクト内のエンティティのデータベーステーブルが作成されます。1つのデータベーステーブルCatalog
のみが作成されます。テーブルデータが追加されます。コマンドからの出力は次のとおりです。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
Catalog has been saved
Catalog has been saved
データベーステーブルの調査
次に、MySQL CLI(コマンドラインインターフェース)から生成されたMySQLデータベーステーブルを調べます。MySQL CLIシェルを起動してmysql
コマンドプロンプトを表示します。
C:\mysql-5.7.25-winx64\mysql-5.7.25-winx64\bin>mysql -u root -p
Enter password: *****
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.7.25 MySQL Community Server (GPL)
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
mysql
として使用するようにデータベースを設定します。
mysql> use mysql
Database changed
テーブルをリスト表示すると、カタログテーブルが表示されます。
mysql> SHOW TABLES;
+---------------------------+
| Tables_in_mysql |
+---------------------------+
| catalog |
…
| user |
+---------------------------+
32 rows in set (0.00 sec)
catalog
テーブルを説明します。Catalog
エンティティフィールドに対応する列が一覧表示されます。 自動生成の主キー列を自動インクメントで使用しているため、id列の値はデータベースによって設定されます。
mysql> DESC catalog;
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| journal | varchar(255) | NO | | NULL | |
| publisher | varchar(255) | NO | | NULL | |
| edition | varchar(25) | NO | | NULL | |
| title | text | NO | | NULL | |
| author | varchar(255) | NO | | NULL | |
| isPublished | tinyint(4) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
7 rows in set (0.01 sec)
SELECT
ステートメントを使用してSQLクエリを実行し、データを一覧表示します。
mysql> SELECT * FROM catalog;
| id | journal | publisher | edition | title
| author | isPublished |
| 1 | Oracle Magazine | Oracle Publishing | March-April 2005 | Starting w
ith Oracle ADF | Steve Muench | 1 |
| 2 | Oracle Magazine | Oracle Publishing | November December 2013 | Engineering as a Service | David A. Kelly | 1 |
2 rows in set (0.01 sec)
mysql>
リポジトリを使用したデータの追加
エンティティに対して操作を実行するには、既に使用したEntityManager
と、このセクションで説明するRepository
の2つのオプションがあります。多数のエンティティを使用する場合は、各エンティティが独自のリポジトリに関連付けられているため、Repository
を使用することをお勧めします。Repository
には、EntityManagerとほとんど同じ機能があります。EntityManager
を使用してCatalogのインスタンスを2つ追加しました。このセクションでは、Repository
を使用して3番目のCatalogインスタンスを追加します。 同じindex.js
を使用しますので、EntityManager
でCatalog
エントリを作成するために使用したコードを削除します。前のセクションのように、Catalog
のインスタンスを作成します。 接続からRepository
インスタンスを取得します。
let catalogRepository = connection.getRepository(Catalog);
save
関数を使用してCatalog
インスタンスを保存します。
await catalogRepository.save(catalog);
変更されたindex.ts
を一覧表示します。
import {createConnection} from "typeorm";
import {Catalog} from "./entity/Catalog";
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "mysql",
database: "mysql",
entities: [
__dirname + "/entity/*.ts"
],
synchronize: true,
logging: false
}).then(async connection => {
let catalog = new Catalog();
catalog.journal = "Oracle Magazine";
catalog.publisher = "Oracle Publishing";
catalog.edition = "November December 2013";
catalog.title = "Quintessential and Collaborative";
catalog.author = "Tom Haunert";
catalog.isPublished = true;
let catalogRepository = connection.getRepository(Catalog);
await catalogRepository.save(catalog);
console.log('Catalog has been saved'+'\n');
let [all_Catalogs, CatalogsCount] = await catalogRepository.findAndCount();
console.log('Catalogs count: ', CatalogsCount+'\n');
}).catch(error => console.log(error));
以前と同じようにアプリケーションを実行すると、別のCatalog
インスタンスが保存されます。 以前に2つのカタログエントリを追加したため、カタログ数3が表示されます。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
Catalog has been saved
Catalogs count: 3
SQLクエリを実行すると、3行のデータが表示されます。
mysql> SELECT * FROM catalog;
| id | journal | publisher | edition | title
| author | isPublished |
| 1 | Oracle Magazine | Oracle Publishing | March-April 2005 | Starting w
ith Oracle ADF | Steve Muench | 1 |
| 2 | Oracle Magazine | Oracle Publishing | November December 2013 | Engineerin
g as a Service | David A. Kelly | 1 |
| 3 | Oracle Magazine | Oracle Publishing | November December 2013 | Quintessen
tial and Collaborative | Tom Haunert | 1 |
3 rows in set (0.00 sec)
mysql>
Connection Managerを使用したエンティティの検索
このセクションでは、EntityManager
クラスを使ってデータを見つけます。EntityManager
には、表2で説明したようにデータを検索するためのいくつかの方法があります。
メソッド |
説明 |
find |
指定されたオプションでエンティティを検索する。 |
findAndCount |
提供されたオプションでエンティティを検索してカウントする。ページネーションオプション(fromとtake)は無視される。 |
findByIds |
指定されたIDのエンティティを検索する。 |
findOne |
オプションに一致する最初のエンティティを見つける。 |
findOneOrFail |
オプションに一致する最初のエンティティを検索し、なければ失敗する。 |
Table 2. EntityManagerメソッドを使ったデータ検索
すべてのCatalog
エンティティを見つけるようにindex.ts
を変更します。すべてのカタログエントリを検索するには、find
メソッドを使用します。
createConnection({
...
...
}).then(async connection => {
let savedCatalogs = await connection.manager.find(Catalog);
console.log("All catalogs from the db: ", savedCatalogs);
}).catch(error => console.log(error));
アプリケーションを実行します。すべてのカタログエントリが表示されます(3番目のエンティティをリポジトリに追加する前にスクリプトが実行されると、2つのエンティティのみが表示されます)。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
All catalogs from the db: [ Catalog {
id: 1,
journal: 'Oracle Magazine',
publisher: 'Oracle Publishing',
edition: 'March-April 2005',
title: 'Starting with Oracle ADF',
author: 'Steve Muench',
isPublished: true },
Catalog {
id: 2,
journal: 'Oracle Magazine',
publisher: 'Oracle Publishing',
edition: 'November December 2013',
title: 'Engineering as a Service',
author: 'David A. Kelly',
isPublished: true } ]
リポジトリを使用したエンティティの検索
このセクションでは、エンティティを見つけるためにRepository
クラスを使います。しかし最初に、JSONオブジェクトをシリアライズ/デシリアライズするために使用されるclass-transformer
パッケージをインストールする必要があります。
C:\Typescript\MySQLProject>npm install class-transformer -g
+ [email protected]
added 1 package from 1 contributor in 10.452s
index.ts
でRepository
のインスタンスを以前のように取得します。まず、エンティティを見つけるためにfind
メソッドを使います。find
メソッドの構文は以下のとおりです。
find(options?: FindManyOptions<Entity>)
FindManyOptions
は、表3で説明した1つ以上のプロパティとして指定されます。
プロパティ |
説明 |
cache |
クエリ結果のキャッシュを有効または無効にする。 |
join |
リレーションをロードするかどうかを指定する。 |
loadEagerRelations |
Eagerリレーションをロードするかどうかを指定する。デフォルトでは、Eagerリレーションはfindメソッドでロードされる。 「Eager」リレーションロードとは、エンティティがロードされたときに関連付けられたモデルのデータをロードすることである。 |
loadRelationIds |
リレーションIDをロードするかどうかを指定する。 trueに設定すると、すべてのリレーションIDがロードされ、リレーション値にマッピングされる。 |
order |
エンティティを並び順を指定する。 |
relations |
エンティティのどのリレーションを読み込むかを指定する。 |
select |
どの列を選択するかを指定する。 |
skip |
スキップするエンティティまたはエンティティからオフセットするエンティティの数を指定する。 |
take |
取得するエンティティの最大数である。 |
where |
適用する条件を指定する。 |
Table 3. FindManyOptionsプロパティ
find
メソッドを使用してエンティティを検索するように index.ts
を変更します。まず、Repository
オブジェクトを取得します。 次に、find
メソッドを使用して、 select
オプションを使用してすべてのエンティティを検索し、title
列とauthor
列のみを選択します。
let allCatalogs = await catalogRepository.find({ select: ["title", "author"] });
結果をJSONとしてシリアル化するには、serialize
メソッドを使用します。
console.log("All Catalogs from the db: ", serialize(allCatalogs)+'\n');
id
が1
のエンティティを検索するには、findOne
メソッドを使用します。serialize
メソッドを使用して結果をJSONとして出力します。
let firstCatalog = await catalogRepository.findOne(1);
console.log('First Catalog from the db: ', serialize(firstCatalog)+'\n');
次に、findOne
メソッドを使用して、title
が「Engineering as a Service」の最初のエンティティを見つけます。
let specificTitleCatalog = await catalogRepository.findOne({ title: "Engineering as a Service"});
結果をJSONとして出力します。
console.log("'Engineering as a Service' Catalog from the db: ", serialize(specificTitleCatalog)+'\n');
edition
が「2013年11月12日」であるすべてのエンティティを検索します。
let allSpecificEditionCatalogs = await catalogRepository.find({ edition: "November December 2013"});
console.log('All November December 2013 Catalogs: ', serialize(allSpecificEditionCatalogs)+'\n');
isPublished
がtrue
であるすべてのエンティティを検索します。
let allPublishedCatalogs = await catalogRepository.find({ isPublished: true });
console.log('All published Catalogs: ', serialize(allPublishedCatalogs)+'\n');
アプリケーションを実行すると結果が表示されます。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
All Catalogs from the db: [{"title":"Starting with Oracle ADF","author":"Steve
Muench"},{"title":"Engineering as a Service","author":"David A. Kelly"},{"title"
:"Quintessential and Collaborative","author":"Tom Haunert"}]
First Catalog from the db: {"id":1,"journal":"Oracle Magazine","publisher":"Ora
cle Publishing","edition":"March-April 2005","title":"Starting with Oracle ADF",
"author":"Steve Muench","isPublished":true}
'Engineering as a Service' Catalog from the db: {"id":2,"journal":"Oracle Magaz
ine","publisher":"Oracle Publishing","edition":"November December 2013","title":
"Engineering as a Service","author":"David A. Kelly","isPublished":true}
All November December 2013 Catalogs: [{"id":2,"journal":"Oracle Magazine","publ
isher":"Oracle Publishing","edition":"November December 2013","title":"Engineeri
ng as a Service","author":"David A. Kelly","isPublished":true},{"id":3,"journal"
:"Oracle Magazine","publisher":"Oracle Publishing","edition":"November December
2013","title":"Quintessential and Collaborative","author":"Tom Haunert","isPubli
shed":true}]
All published Catalogs: [{"id":1,"journal":"Oracle Magazine","publisher":"Oracle
Publishing","edition":"March-April 2005","title":"Starting with Oracle ADF","author":"Steve
Muench","isPublished":true},{"id":2,"journal":"Oracle Magazine","publisher":"Oracle
Publishing","edition":"November December 2013","title":"Engineering as a Service","author":"David A.
Kelly","isPublished":true},{"id":3,"journal":"Oracle Magazine","publisher":"Oracle
Publishing","edition":"November December 2013","title":"Quintessential and
Collaborative","author":"Tom Haunert","isPublished":true}]
エンティティの更新
このセクションでは、Catalog
エンティティを更新します。EntityManager
クラスとRepository
クラスの両方に、エンティティを部分的に更新するためのupdate
メソッドが用意されています。update
メソッドは高速で効率的ですが、以下の制限があります。
- カスケード、リレーション、その他の操作を含めずに基本操作を実行する。
- エンティティがデータベースに存在するかどうかを確認しない。
save
メソッドはエンティティが既に存在する場合は更新し、存在しない場合は新しいエンティティを追加します。save
メソッドには、update
にある制限がありません。例として、&ldquo;から、id 1
のCatalog
エンティティのtitleを「Starting with Oracle ADF」から「Beginning with Oracle ADF」に更新します。Catalog
エンティティのRepository
のインスタンスを取得します。
id
が1のエンティティを探します。
let catalogToUpdate = await catalogRepository.findOne(1);
title
を「Beginning with Oracle ADF」に設定します。
catalogToUpdate.title = "Beginning with Oracle ADF";
save
メソッドを使用してエンティティを保存します。
await catalogRepository.save(catalogToUpdate);
その後、id
が1のエンティティをもう一度検索します。
let updatedCatalog = await catalogRepository.findOne(1);
更新されたエンティティをJSONとして出力します。
console.log('Updated Catalog from the db: ', serialize(updatedCatalog)+'\n');
アプリケーションを実行すると、エンティティが更新されます。更新されたエンティティが出力されます。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
First Catalog from the db: {"id":1,"journal":"Oracle Magazine","publisher":"Ora
cle Publishing","edition":"March-April 2005","title":"Beginning with Oracle ADF"
,"author":"Steve Muench","isPublished":true}
エンティティの削除
このセクションでは、エンティティを削除します。EntityManager
クラスとRepository
クラスの両方に、エンティティを削除するための表4で説明されているメソッドが用意されています。
メソッド |
説明 |
delete |
条件を使用してエンティティを削除する。条件は空にできない。更新メソッドと同じ欠点がある。エンティティが存在するかどうかを確認しないことと、カスケードと関係が含まない。その前提を許容できれば、削除は速くて効率的である。 |
remove |
データベースからエンティティを削除する。 |
Table 4. エンティティを削除するためのメソッド
index.ts
を変更してエンティティの削除を実演します。Catalog
エンティティのためのRepository
のインスタンスを以前と同様に取得します。
findOne
メソッドを使用して、idが1のエンティティを検索します。
let catalogToRemove = await catalogRepository.findOne(1);
remove
メソッドを使用してエンティティを削除します。
await catalogRepository.remove(catalogToRemove);
その後、 find
を実行して同じエンティティを出力し、削除されたかどうかを確認します。 削除されている場合、結果にエンティティが表示されません。
let firstCatalog = await catalogRepository.findOne(1);
console.log("First Catalog from the db: ", serialize(firstCatalog));
アプリケーションを実行すると、findOne
の結果はundefined
です。これはエンティティが削除されたことを意味します。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
First Catalog from the db: undefined
MySQL CLIでSQLクエリを実行すると、idが1のデータ行が表示されていません。
mysql> SELECT * FROM catalog;
| id | journal | publisher | edition | title
| author | isPublished |
| 2 | Oracle Magazine | Oracle Publishing | November December 2013 | Engineerin
g as a Service | David A. Kelly | 1 |
| 3 | Oracle Magazine | Oracle Publishing | November December 2013 | Quintessen
tial and Collaborative | Tom Haunert | 1 |
2 rows in set (0.00 sec)
mysql>
エンティティ間に一対一のリレーションの作成
TypeORMはエンティティ間のいくつかの種類の関係をサポートします。
- 1対1
- 1対多、多対1
- 多対多
表5で説明されているように、これらのそれぞれに対してデコレータと関数がtypeormで提供されています。
関数 |
説明 |
OneToOne |
エンティティ間の1対1の関係を指定する |
JoinColumn |
1対1リレーションの所有側を指定する |
OneToMany |
エンティティ間の1対多の関係を指定する |
ManyToOne |
エンティティ間の多対1の関係を指定する |
ManyToMany |
エンティティ間の多対多の関係を指定する |
JoinTable |
多対多関係の所有側を指定する |
Table 5. リレーションのための関数
このセクションでは、2つのエンティティ間の1対1の関係について説明します。Catalog
のタイムスタンプ用で2番目のエンティティCatalogTimestamp
を定義します。entity
ディレクトリにCatalogTimestamp.ts
スクリプトを作成します。他の関数に加えて、OneToOne
とJoinColumn
関数をインポートします。
import {Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
import {Catalog} from "./Catalog";
CatalogTimestamp
クラスをエクスポートし、@Entity()
>デコレータを使用してクラスをエンティティにします。
@Entity()
export class CatalogTimestamp {
…
}
主キー列idを宣言します。
@PrimaryGeneratedColumn()
id: number;
firstAdded
、firstUpdated
とlastUpdated
列を追加し、すべてstring
型とします。
@Column()
firstAdded: string;
@Column()
firstUpdated: string;
@Column()
lastUpdated: string;
@OneToOne
デコレータを使用して、Catalog
エンティティと1対1の関係を追加します。リレーションは、単方向または双方向です。双方向リレーションを指定します。type => Catalog
は、あるいは() => Catalog
と指定することもできますが、1対1のリレーションが存在するエンティティのクラスを返します。関数catalog => catalog.timestamp
はリレーションの逆側を返します。@JoinColumn()
デコレータは、1対1リレーションの所有側を指定します。 一方の側だけが所有側になることができます。
@OneToOne(type => Catalog, catalog => catalog.timestamp)
@JoinColumn()
catalog: Catalog;
CatalogTimestamp
エンティティは、GitHubに表示されます。リストをCatalogEntity.ts
スクリプトにコピーします。
CatalogTimestamp
との1対1のリレーションを指定するためにCatalog
エンティティを変更する必要があります。typeormから他の関数に加えて、OneToOne
とJoinColumn
関数をインポートします。CatalogTimestamp
クラスをインポートします。
import {Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
import {CatalogTimestamp} from "./CatalogTimestamp";
Catalog
エンティティの残りの部分は、@OneToOne
デコレータを宣言する必要があることを除けば、以前と同じです。type => CatalogTimestamp
関数は、リレーションが存在するエンティティを指定します。timestamp => timestamp.catalog
関数は逆のリレーションを指定します。cascade
オプションをtrue
に設定すると、エンティティが保存されるたびに関連するエンティティが保存されます。これは、Catalog
のインスタンスが保存されている場合は関連付けられたCatalogTimestamp
エンティティも保存されることを意味します。
@OneToOne(type => CatalogTimestamp, timestamp => timestamp.catalog,{
cascade: true,
})
timestamp: CatalogTimestamp;
次に、Catalog
エンティティとCatalogTimestamp
エンティティを使用して1対1の関係を作成するようにindex.ts
を変更します。CatalogTimestamp
クラスを追加でインポートします。
import {CatalogTimestamp} from "./entity/CatalogTimestamp";
以前のようにCatalog
エンティティインスタンスを作成します。
let catalog = new Catalog();
catalog.journal = "Oracle Magazine";
catalog.publisher = "Oracle Publishing";
catalog.edition = "March-April 2005";
catalog.title = "Starting with Oracle ADF";
catalog.author = "Steve Muench";
catalog.isPublished = true;
さらに、CatalogTimestamp
エンティティインスタンスを作成します。
let timestamp = new CatalogTimestamp();
timestamp.firstAdded = "Apr-8-2014-7:06:16-PM-PDT";
timestamp.firstUpdated = "Apr-8-2014-7:06:20-PM-PDT";
timestamp.lastUpdated = "Apr-8-2014-7:06:20-PM-PDT";
CatalogTimestamp
とCatalog
エンティティインスタンスを関連付けるか接続します。
timestamp.catalog = catalog;
Catalog
のエンティティリポジトリを取得します。
let catalogRepository = connection.getRepository(Catalog);
save
メソッドを使用してCatalog
エンティティを保存します。
await catalogRepository.save(catalog);
console.log("Catalog has been saved");
修正されたCatalog.ts
スクリプトはGitHubプロジェクトに一覧化されています。
アプリケーションを実行すると、それらの間のリレーションを含むエンティティが保存されます。cascade
を指定したため、CatalogTimestamp
エンティティが保存されます。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
Catalog has been saved
Timestamp is saved, and relation between timestamp and catalog is created in the database too
リレーションを持つオブジェクトの検索
この節では、リレーションがあるオブジェクトを検索します。EntityManager
またはRepository
のfind*
メソッドを使用して、リレーションを持つオブジェクトを見つけることができます。index.ts
を変更します。Catalog
のRepository
インスタンスを取得します。リレーションを含むすべてのエンティティを見つけます。find
メソッドのFindManyOptions
オプションのrelations
プロパティは、timestamp
リレーションを指定します。
let catalogs = await catalogRepository.find({relations: ["timestamp"]});
結果をJSONとして出力します。
console.log(serialize(catalogs));
アプリケーションを実行して、新しく追加されたCatalog
エンティティと、関連付けられているCatalogTimestamp
エンティティを出力します。 3つのCatalogエンティティのうち1つだけが関連付けられたnullでないtimestamp
を持ちます。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
[{"id":6,"journal":"Oracle Magazine","publisher":"Oracle Publishing","edition":"
March-April 2005","title":"Starting with Oracle ADF","author":"Steve Muench","is
Published":true,"timestamp":{"id":1,"firstAdded":"Apr-8-2014-7:06:16-PM-PDT","fi
rstUpdated":"Apr-8-2014-7:06:20-PM-PDT","lastUpdated":"Apr-8-2014-7:06:20-PM-PDT
"}},{"id":2,"journal":"Oracle Magazine","publisher":"Oracle Publishing","edition
":"November December 2013","title":"Engineering as a Service","author":"David A.
Kelly","isPublished":true,"timestamp":null},{"id":3,"journal":"Oracle Magazine"
,"publisher":"Oracle Publishing","edition":"November December 2013","title":"Qui
ntessential and Collaborative","author":"Tom Haunert","isPublished":true,"timest
amp":null}]
FindOneOptions
およびFindManyOptions
とともにfind
*メソッドを使用する場合、ほとんどのクエリに適していますが、QueryBuilder
の方が、WHERE
句、HAVING
句、ORDER BY
句、GROUP BY
句、LIMIT
句、OFFSET
句などを含む検索でエンティティのサブセットを特定するオプションや設定が増えるため、複雑なクエリに適しています。さらにリレーションの結合、内部結合および左結合、選択なしで結合 、結合とマッピング機能、ページネーション、サブクエリも指定できる。例として、リポジトリを取得し、QueryBuilder
を作成します。innerJoinAndSelect
でINNER JOIN
を指定します。複数の結果を取得するにはgetMany
を使用してください。結果を1つだけ取得するには、getOne
を使用する必要があります。
let catalogs = await connection
.getRepository(Catalog)
.createQueryBuilder("catalog")
.innerJoinAndSelect("catalog.timestamp", "timestamp")
.getMany();
console.log(serialize(catalogs));
CatalogTimestamp
と1対1の関係にあるCatalog
エンティティを出力するようにアプリケーションを実行します。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
[{"id":6,"journal":"Oracle Magazine","publisher":"Oracle Publishing","edition":"
March-April 2005","title":"Starting with Oracle ADF","author":"Steve Muench","is
Published":true,"timestamp":{"id":1,"firstAdded":"Apr-8-2014-7:06:16-PM-PDT","fi
rstUpdated":"Apr-8-2014-7:06:20-PM-PDT","lastUpdated":"Apr-8-2014-7:06:20-PM-PDT
"}}]
MySQL CLIからmysql
データベース内のMySQLテーブルを一覧表示し、catalog
とcatalog_timestamp
テーブルを一覧表示します。
mysql> use mysql
Database changed
mysql> show tables;
+---------------------------+
| Tables_in_mysql |
| catalog |
| catalog_timestamp |
catalog_timestamp
テーブルを記述すると、他の列に加えて外部キーcatalogId
が表示されます。
mysql> DESC catalog_timestamp;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| firstAdded | varchar(255) | NO | | NULL | |
| firstUpdated | varchar(255) | NO | | NULL | |
| lastUpdated | varchar(255) | NO | | NULL | |
| catalogId | int(11) | YES | UNI | NULL | |
+--------------+--------------+------+-----+---------+----------------+
5 rows in set (0.02 sec)
catalog_timestamp
に対してSELECT
ステートメントを実行すると、クエリ結果にtimestamp
が関連づけられているcatalog
テーブル行の外部キー列 id
の値6が含まれます。
mysql> SELECT * FROM catalog_timestamp;
| id | firstAdded | firstUpdated | lastUpdated
| catalogId |
| 1 | Apr-8-2014-7:06:16-PM-PDT | Apr-8-2014-7:06:20-PM-PDT | Apr-8-2014-7:06:2
0-PM-PDT | 6 |
1 row in set (0.00 sec)
catalog
でSELECT
ステートメントを実行すると、id
が6の行が表示されます。サンプルデータは異なる場合がありますが、外部キー値はtimestamp
に関連付けられています。
mysql> SELECT * FROM catalog;
| id | journal | publisher | edition | title
| author | isPublished |
| 2 | Oracle Magazine | Oracle Publishing | November December 2013 | Engineerin
g as a Service | David A. Kelly | 1 |
| 3 | Oracle Magazine | Oracle Publishing | November December 2013 | Quintessen
tial and Collaborative | Tom Haunert | 1 |
| 6 | Oracle Magazine | Oracle Publishing | March-April 2005 | Starting w
ith Oracle ADF | Steve Muench | 1 |
3 rows in set (0.00 sec)
同様に、1対多および多対多の関係を使用できます。次に、1対多の関係の例を説明します。
1対多リレーションの作成
1対多の使い方を説明するには、もう一度2つのエンティティが必要です。このセクションでは、CatalogEntry
と1対多であるCatalogEdition
エンティティを使用します。CatalogEdition
エンティティは、プライマリ列idに加えて、editionとisPublished
の列を指定します。@OneToMany
デコレータは、CatalogEntry
と1対多の関係を、その逆の関係を含めて定義します。双方向リレーションが定義されます。
@OneToMany(type => CatalogEntry, catalogEntry => catalogEntry.catalogEdition)
catalogEntries: CatalogEntry[];
CatalogEdition
エンティティは、GitHubプロジェクトに一覧化されています。
CatalogEntry
エンティティは、プライマリ列idに加えて、title, author, isPublished
列を指定します。@ManyToOne
デコレータは、逆関係を含むCatalogEdition
との多対1の関係を指定します。
@ManyToOne(type => CatalogEdition, catalogEdition => catalogEdition.catalogEntries)
catalogEdition: CatalogEdition;
CatalogEntry
エンティティは、GitHubプロジェクトに一覧表示されます。
index.ts
を次のように変更します。
import "reflect-metadata";
import {createConnection} from "typeorm";
import {CatalogEdition} from "./entity/Edition";
import {CatalogEntry} from "./entity/Section";
import {serialize} from "class-transformer";
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "mysql",
database: "mysql",
entities: [
__dirname + "/entity/*.ts"
],
synchronize: true,
logging: false
}).then(async connection => {
// Create entity instances and save data to entities
}).catch(error => console.log(error));
アプリケーションを実行してテーブルとリレーションを作成します。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
mysql
データベースのテーブルを一覧表示すると、もしあれば他のテーブルと共に、catalog_edition
とcatalog_entry
テーブルが表示されます。
mysql> show tables;
+---------------------------+
| Tables_in_mysql |
+---------------------------+
| catalog |
| catalog_edition |
| catalog_entry |
| catalog_timestamp |
catalog_edition
テーブルを表示すると、id
、edition、isPublished
の3つの列が表示されます。
mysql> DESCRIBE catalog_edition;
| Field | Type | Null | Key | Default | Extra |
| id | int(11) | NO | PRI | NULL | auto_increment |
| edition | varchar(255) | NO | | NULL | |
| isPublished | tinyint(4) | NO | | NULL | |
3 rows in set (0.01 sec)
catalog_entry
テーブルを表示すると、外部キー列catalogEditionId
などが表示されます。
mysql> DESCRIBE catalog_entry;
| Field | Type | Null | Key | Default | Extra |
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(255) | NO | | NULL | |
| author | varchar(255) | NO | | NULL | |
| isPublished | tinyint(4) | NO | | NULL | |
| catalogEditionId | int(11) | YES | MUL | NULL | |
5 rows in set (0.01 sec)
多対多リレーションの作成
このセクションでは、エンティティ間に多対多の関係を作成するデモを示します。そのために2つのエンティティが必要になります。2つの新しいエンティティEdition
とSection
を使用します。Sectionエンティティは列id
とname
を定義します。Edition
との多対多の双方向リレーションは、@ManyToMany
デコレータを使用して次のように定義されます。
@ManyToMany(type => Edition, edition => edition.sections)
editions: Edition[];
Section
エンティティはGitHubプロジェクトに一覧表示されています。
Edition
エンティティは、 id
とname
の列を指定します。@ManyToMany
デコレータは、Section
との関係を、その逆の関係も含めて定義します。@JoinTable()
デコレータは、リレーションの所有側を示し、片側だけが所有できます。
@ManyToMany(type => Section, section => section.editions)
@JoinTable()
sections: Section[];
Edition
エンティティはGitHubプロジェクトに一覧表示されています。
Edition
とSection
の間の多対多の関係を示すために index.ts
を変更します。Edition
エンティティのインスタンスを2つ作成してデータベースに保存します。
let edition1 = new Edition();
edition1.name = "January February 2019";
await connection.manager.save(edition1);
let edition2 = new Edition();
edition2.name = "November December 2018";
await connection.manager.save(edition2);
Section
エンティティのインスタンスを作成します。
let section = new Section();
section.name = "Application Development";
editions
リレーションをedition1
とedition2
に設定します。
section.editions = [edition1, edition2];
Section
エンティティをデータベースに保存します。
await connection.manager.save(section);
別のSection
エンティティインスタンスを作成し、リレーションeditions
を1つのエディションedition1
に設定します。Section
エンティティインスタンスを保存します。
let section2 = new Section();
section2.name = "DBA";
section2.editions = [edition1];
await connection.manager.save(section2);
3番目のSection
エンティティインスタンスを作成し、edition1
に関連付けてデータベースに保存します。次に、findOne
メソッドを使用して、editions
リレーションによって関連付けられたEdition
エンティティを含む1つのSection
エンティティを検索します。
const loadedSection = await connection
.getRepository(Section)
.findOne(1, { relations: ["editions"] });
結果をJSONとして出力します。
console.log(serialize(loadedSection));
修正されたindex.ts
はGitHubプロジェクトに一覧表示されています。
アプリケーションを実行して、Edition
とSection
の間に多対多の関係を作成します。id
が1のSection
エンティティおよび関連するEdition
もJSONとして出力されます。
C:\Typescript\MySQLProject>npm start
> [email protected] start C:\Typescript\MySQLProject
> ts-node src/index.ts
{"id":1,"name":"Application Development","editions":[{"id":2,"name":"January Feb
ruary 2019"},{"id":3,"name":"November December 2018"}]}
テーブルを一覧表示すると、edition
およびsection
のテーブルが表示されます。 ジャンクションテーブルedition_sections_section
も表示されます。
mysql> show tables;
| Tables_in_mysql |
| edition |
| edition_sections_section |
| section |
まとめ
TypeScriptはJavaScriptの型付けされたオープンソーススーパーセットであり、静的型付け、型アノテーション、型推論に加えてインタフェースなどのオブジェクト指向の機能を提供します。TypeScriptには、クラスをデコレート/アノテーションするためのDecoratorsという実験的な機能と、追加機能を提供するためのクラスメンバーが含まれています。TypeScriptは現代の大規模アプリケーション用に設計されています。この記事では、MySQLデータベースでTypeScriptを使用する方法について説明しました。TypeORMライブラリは、データベースとの接続、エンティティの作成と保存、エンティティ間の関係の作成、エンティティの検索、エンティティの更新と削除に使用されます。Node.js上で動作し、MySQLデータベースに接続して雑誌カタログをモデル化する、エンドツーエンドのオブジェクトリレーショナルマッピングアプリケーションでTypeScriptを使用する方法を示しました。
著者について
Deepak Vohra氏は、Sun認定のJavaプログラマーおよびSun認定のWebコンポーネント開発者です。Deepak氏は、JavaおよびJava EEに関連する技術記事をWebLogic Developer's Journal、XML Journal、ONJava、java.net、IBM developerWorks、Java Developer's Journal、Oracle Magazine、devxに公開しています。Deepak氏は、Asynchronous JavaScript and XMLについての本Ajax in Oracle JDeveloperも出版しています。