Node.jsでsqlite3モジュールを使ってみる

# はじめに
Node.jsを使ってDB操作をしてみたかったため，sqlite3モジュールを使ってみました。
こちらの記事は，その際の備忘録となります。

# 大まかな流れ
1. 必要なパッケージのダウンロード
2. 実行ファイルの作成
3. 実行

以下で一つ一つについて詳細に説明します。

# 1. 必要なパッケージのダウンロード
今回は，sqlite3モジュールを利用しますので，以下のコマンドでパッケージをダウンロードします。

```console:npm5.0.0以降を利用している場合
$ npm install sqlite3 
```

```console:npm5.0.0より前のバージョンを利用している場合
$ npm install --save sqlite3 
```
- `--save` オプション : package.jsonのdependenciesに追加される（npm 5.0.0 以降ではデフォルト）

# 2. 実行ファイルの作成
今回は、ダミーのデータベースを作成し、その中身をコンソールログに出力するようなサンプルを作成します。

```js:sample.js
// .verbose() をつけると、デバッグしやすくなる
const sqlite3 = require('sqlite3').verbose();

// sqlite3.Database() の第一引数に ':memory:' を指定することで、メモリ上にDBを作成することができる
const db = new sqlite3.Database(':memory:');

// db is instance of Database class
console.log(db instanceof sqlite3.Database); // => true

// DB操作を行う処理を serializeメソッドのコールバック関数内で記述することで、クリエを順に実行することができる
db.serialize(function() {

  // 指定したクエリを実行する
  db.run("CREATE TABLE dummy (info TEXT)");

  // Statementオブジェクトを作成する
  const statement = db.prepare("INSERT INTO dummy VALUES (?)");

  // statement is instance of Statement class
  console.log(statement instanceof sqlite3.Statement); // => true

  for (let i = 0; i < 10; i++) {

    // prepareメソッドで指定したSQL文中の ? に値を設定し、SQLを実行する
    statement.run("number" + i);
  }

  // statementオブジェクトを終了する
  statement.finalize();

  // 結果を取得する毎にコールバック関数を実行する
  db.each("SELECT rowid AS id, info FROM dummy", function(err, row) {
    console.log(row.id + ": " + row.info);
  });

});

// DBをクローズする
db.close();
```
以下で詳細に説明します。

### sqlite3モジュールを読み込む
```js:sample.js（一部）
const sqlite3 = require('sqlite3').verbose();
```
requireでsqlite3モジュールを読み込みます。
**verbose**メソッドについては、下記を参照してください。
参照 : https://github.com/mapbox/node-sqlite3/wiki/API#sqlite3verbose

### 新規DBを作成
```js:sample.js（一部）
const db = new sqlite3.Database(':memory:');
```
**Database**クラスの新規インスタンスを作成します。
第一引数に `:memory:` を指定することで、メモリ上に一時的なDBを作成することができます。
`:memory:` ではなくファイル名を指定すれば、永続的なDBを扱うこともできます。
参照 : https://github.com/mapbox/node-sqlite3/wiki/API#new-sqlite3databasefilename-mode-callback

### DB操作を上から順に実行する
```js:sample.js（一部）
db.serialize(function() {
...
}
```
**serialize**メソッドを使用することで、引数に指定された関数内の処理を、順に実行します。
参考 : https://github.com/mapbox/node-sqlite3/wiki/Control-Flow#databaseserializecallback

並列に実行したい場合は、[**parallelizeメソッド**](https://github.com/mapbox/node-sqlite3/wiki/Control-Flow#databaseparallelizecallback)を使用してください。

### クエリを実行する
```js:sample.js（一部）
db.run("CREATE TABLE dummy (info TEXT)");
```
**run**メソッドは、引数で指定したクエリを実行します。
参考 : https://github.com/mapbox/node-sqlite3/wiki/API#databaserunsql-param--callback

### Statementオブジェクトの作成・実行
```js:sample.js（一部）
const statement = db.prepare("INSERT INTO dummy VALUES (?)");
for (let i = 0; i < 10; i++) {
  statement.run("number" + i);
}
statement.finalize();
```
**prepare**メソッドを使用して、**Statement**クラスの新規インスタンスを作成します。
Statementオブジェクト作成後、**run**メソッドを使用することで、prepareメソッドの第一引数で指定したクエリの `?` の箇所に値を代入してクエリを実行することができます。
Statementオブジェクトの使用後は、**finalize**メソッドで終了させます。
参考 : https://github.com/mapbox/node-sqlite3/wiki/API#statement

### DBの中身の取得
```js:sample.js（一部）
db.each("SELECT rowid AS id, info FROM dummy", function(err, row) {
  console.log(row.id + ": " + row.info);
});
```
**each**メソッドを使用して、クエリの実行結果1行毎に行う処理を記述します。
参考 : https://github.com/mapbox/node-sqlite3/wiki/API#databaseeachsql-param--callback-complete

似たようなメソッドに、**all**メソッドがあります。
allメソッドは、全ての結果を一度に配列に格納するため、**取得する件数がある程度わかっている場合**には有効です。
しかし、**取得件数が膨大であったりあまり予測できなかったりする場合**は、メモリを使用しすぎる可能性があるため、eachメソッドの方が有効です。
参考 : https://github.com/mapbox/node-sqlite3/wiki/API#databaseallsql-param--callback

### DBをクローズ
```js:sample.js（一部）
db.close();
```
**close**メソッドを使用して、データベースをクローズします。
参考 : https://github.com/mapbox/node-sqlite3/wiki/API#databaseclosecallback

# 3. 実行
```console
$ node sample.js
1: number0
2: number1
3: number2
4: number3
5: number4
6: number5
7: number6
8: number7
9: number8
10: number9
```

# 参考

### 公式
- [GitHub](https://github.com/mapbox/node-sqlite3)
- [APIドキュメント](https://github.com/mapbox/node-sqlite3/wiki/API)

### その他
- [Node.jsでsqlite3データベース処理を行う](https://www.usagi1975.com/20161226-2055/)