Dexie.jsのトランザクションではまる

# Dexie.jsとは？

IndexedDBを簡単に扱うためのラッパーライブラリ(実は簡単ではなかったというお話:sob:)

http://dexie.org/

# IndexedDBとは？

簡潔に(雑に)表現すると、ブラウザ内のRDBMS。

https://developer.mozilla.org/ja/docs/Web/API/IndexedDB_API

上記リンク先にも書いてあるが、ローレベルなAPIなので、
ラッパーライブラリを使ったほうが実装が簡単。
今回Dexie.jsを使ったのはドキュメントが豊富だから。

http://dexie.org/docs/

# Dexie.jsのインストール

```
$ yarn add dexie
```

終了！！！

# Dexie.jsでのトランザクション

トランザクションではまってしまったのでその例を以下に示す。
まずは以下のrappersテーブルが既にあるものとする。

|id |name|sort|
|:--|----|----|
|1|Ad Rock|1|
|2|Biz Markie|2|
|3|Common|3|

アドロックが俺はラッパーじゃないと言い出したのでテーブルから削除するトランザクションを考える。
ここで必要なのは削除と他のsortカラムの更新(デクリメント)である。
こういう処理はよくあると思う。
これをDexie.jsのトランザクションで書いてみる。

```
import Dexie from 'dexie';

const db = new Dexie('hiphop');

db.version(1).stores({
    rappers: '++id, name, sort'
});

db.rappers.bulkAdd([
    { name: 'Ad Rock', sort: 1 },
    { name: 'Biz Markie', sort: 2 },
    { name: 'Common', sort: 3 }
]);

db.transaction('rw', db.rappers, () => {
    db.rappers.get({ name: 'Ad Rock' }).then( adrock => {
        db.rappers.delete(adrock.id);
        db.rappers.where('sort').above(adrock.sort).each( rapper => {
            db.rappers.update(rapper.id, { sort: rapper.sort - 1 }).then( updated => {
                if (!updated) {
                    console.log('not updated.');
                }
            });
        });
    });
}).then( () => {
    console.log('committed');
}).catch( error => {
    console.log(error);
});
```

|id |name|sort|
|:--|----|----|
|2|Biz Markie|1|
|3|Common|2|

実行すると正常に削除と更新がされる。

ここでトランザクション内で無理やりエラーを発生させてみよう。

```
db.transaction('rw', db.rappers, () => {
    db.rappers.get({ name: 'Ad Rock' }).then( adrock => {
        db.rappers.delete(adrock.id);
        db.rappers.where('sort').above(adrock.sort).each( rapper => {
            db.rappers.update(rapper.id, { sort: rapper.sort - 1 }).then( updated => {
                if (!updated) {
                    console.log('not updated.');
                }
            });
        }).then( () => {
            let z = a;  // エラー
        });
    });
}).then( () => {
    console.log('committed');
}).catch( error => {
    console.log(error);
});
```

|id |name|sort|
|:--|----|----|
|1|Ad Rock|1|
|2|Biz Markie|2|
|3|Common|3|

ロールバックされ、データは更新されない。いい感じです。

次に、Dexie.jsにはTransaction.abort()というメソッドがある。

http://dexie.org/docs/Transaction/Transaction.abort()

これを実行すると、

> Dexie.currentTransaction.abort(); // Will discard all changes.

らしい。ロールバックされて強制終了するのではと想像する。

```
db.transaction('rw', db.rappers, () => {
    db.rappers.get({ name: 'Ad Rock' }).then( adrock => {
        db.rappers.delete(adrock.id);
        db.rappers.where('sort').above(adrock.sort).each( rapper => {
            db.rappers.update(rapper.id, { sort: rapper.sort - 1 }).then( updated => {
                if (!updated) {
                    console.log('not updated.');
                }
            });
        }).then( () => {
            Dexie.currentTransaction.abort();
        });
    });
}).then( () => {
    console.log('committed');
}).catch( error => {
    console.log(error);
});
```

|id |name|sort|
|:--|----|----|
|2|Biz Markie|1|
|3|Common|2|

処理が中断されたことはコンソールでわかるけど、IndexedDBの中身を見ると、ロールバックされずにデータが削除更新されている。
abort()はロールバックしないということなのだろうか。
"Will discard all changes"じゃなくない？？
それともトランザクションの記述の仕方が間違っているのだろうか。
とりあえず、abort()ではなく本当のエラーが起きたときはロールバックされているのでいいが、
プログラマブルにロールバックを実行できないのはもやっとする。
以上です:microphone: