ブラウザのDBにアクセスする

ブラウザに情報を保存する仕組みはいくつかありますが、その中の一つであるWeb SQL DatabaseというAPIを紹介します。

# 目的
- Web SQL Databaseを知る
- TODOアプリを作る

# Web SQL Databaseとは
冒頭でも言いましたが、ブラウザにデータを保存する仕組みの一つです。CookieやWeb StrageのKVS形式とは違い、SQLでデータの保存、呼び出しをすることができるSQLiteベースのブラウザDBとなります。残念ながらHTML5の標準仕様に入ることはなかったものの、多くの主要ブラウザ（Google Chrome、Safari、Firefox、Androidブラウザなど）でサポートされている機能です。
![webdb.JPG](https://qiita-image-store.s3.amazonaws.com/0/169092/ebd12349-58bf-e9af-876c-09cd8e389d7a.jpeg)
DBはサーバサイドで管理することが多いと思いますが、クライアントにそれなりの情報を持たせるとなると用途が難しそうだし、サーバサイドとの同期をとったり管理が大変そう。けど、上手く使えばサーバアクセスの回数を減らしてサーバ負荷を軽減することができそうですね。クライアントにDBがあるので、通信速度にストレスを感じることもなさそうです。

# 基本操作
基本となる操作を紹介します。実装はJavaScriptで行います。
## DBを開く

```javascript
var name = 'localdb'
var version = '1.0'
var description = 'Web SQL Database'
var size = 2 * 1024 * 1024
var db = openDatabase(name, version, description, size)
```
openDatabaseメソッドに引数を渡して実行し、データベースオブジェクトを作成します。指定したDBが存在するならばそのDBを、存在しないならば作成して開きます。

## クエリの実行

```javascript
var db = openDatabase(name, version, description, size)

db.transaction(function (tx) {
  tx.executeSql(SQL, [パラメータ], 成功コールバック, 失敗コールバック)
})
```
こんな感じでクエリを実行します。引数はSQLだけでも実行できます。

### 検索

```javascript
db.transaction(function (tx) {
  tx.executeSql('select id, name from user', [],
    function (tx, results) {
      // SUCCESS
      for (i = 0; i < results.rows.length; i++){
        console.log(results.rows.item(i).id)
        console.log(results.rows.item(i).name)
      }
    },
    function(err){
      // ERROR
    })
})
```
こんな感じで検索結果を取得します。

### 追加 更新 削除
```javascript
db.transaction(function (tx) {
  tx.executeSql('insert into user (id, name) values (?, ?)', [1, '金田'])
  tx.executeSql('update user set name = ? where id = ?', ['島', 1])
  tx.executeSql('delete from user where id = ?', [1])
})
```
更新系はこんな感じになります。

# TODOアプリ
ここまでの基本操作を使って今回はTODOアプリを作りました。ソースは[GitHub](https://github.com/hys-rabbit/WebSqlDatabase)にあります。
ディレクトリ構成はこんな感じ。

    WebSqlDatabase
    ├── bundle.js <- src下をbrowserifyしたやつ
    ├── index.html
    └── src
        ├── Index.js
        ├── TodoDao.js
        └── jquery-3.2.1.min.js <- ダウンロードしとく
画面にはTODOを入力するテキストボックスと登録するボタン、それと登録済みのTODOを表示するテーブルを用意します。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>Web SQL Database</title>
</head>
<body>
<input type="text" name="todo" />
<button type="button" name="register">登録</button>
<table id="table">
  <thead>
    <tr>
      <th>No</th>
      <th>Todo</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <!-- TODOを追加していく -->
  </tbody>
</table>

  <script src="bundle.js"></script>
</body>
</html>
```
ボタンを押下するとTODOを登録し、テーブルに表示します。登録されたTODOと同じ並びに更新と削除ボタンを用意します。更新はテキストボックスに入力されたTODOで更新します。

```javascript:Index.js
var TodoDao = require('./TodoDao')
var $ = require('./jquery-3.2.1.min')

// DAOインスタンス
var tododao = new TodoDao()

$(document).ready(function(){
  // イベントハンドラ登録
  $('input[name=todo]').keyup(function(v){
    $('button[name=register], button[name=edit]')
      .prop('disabled', $(this).val() == 0)
  })
  $('button[name=register]').on('click', register)
  $('#table tbody').on('click', 'tr td button[name=edit]', edit)
  $('#table tbody').on('click', 'tr td button[name=remove]', remove)

  init()
})

// 初期化（全件表示）
var init = function(){
  // TODO表の削除
  $('#table tbody').empty()
  // TODO表の表示
  tododao.findAll(function(list){
    $.each(list, function(i, e){
      $('#table tbody').append(`
        <tr>
          <td>${i+1}</td>
          <td>${e.todo}</td>
          <td><button type="button" name="edit" value="${e.id}">更新</button></td>
          <td><button type="button" name="remove" value="${e.id}">削除</button></td>
        </tr>
      `)
    })

    // TODOテキストボックス、ボタンの初期化
    $('input[name=todo]').val('').focus().keyup()
  })
}

// 登録
var register = function(){
  var todo = $('input[name=todo]').val()
  tododao.insert(todo, init)
}

// 更新
var edit = function(){
  var id = $(this).val()
  var todo = $('input[name=todo]').val()
  tododao.update(id, todo, init)
}

// 削除
var remove = function(){
  var id = $(this).val()
  tododao.remove(id, init)
}
```
DAOは別ファイルにしています。
# 

```javascript:TodoDao.js
var TodoDao = function(){
  var name = 'localdb'
  var version = '1.0'
  var description = 'Web SQL Database'
  var size = 2 * 1024 * 1024
  var db = openDatabase(name, version, description, size)

  // テーブル作成
  db.transaction(function(tx){
    tx.executeSql(`
      create table if not exists todo (
        id integer primary key autoincrement,
        todo varchar
      )
    `)
  })

  // 全件検索
  this.findAll = function(callback){
    db.transaction(function (tx){
      tx.executeSql('select * from todo', [],
        function (tx, results){
          var list = []
          for (i = 0; i < results.rows.length; i++){
            list.push({
              id: results.rows.item(i).id,
              todo: results.rows.item(i).todo
            })
          }
          callback(list)
        })
    })
  }

  // 登録
  this.insert = function(todo, callback){
    db.transaction(function (tx){
      tx.executeSql('insert into todo (todo) values (?)', [todo], callback)
    })
  }

  // 更新
  this.update = function(id, todo, callback){
    db.transaction(function (tx){
      tx.executeSql('update todo set todo = ? where id = ?', [todo, id], callback)
    })
  }

  // 削除
  this.remove = function(id, callback){
    db.transaction(function (tx){
      tx.executeSql('delete from todo where id = ?', [id], callback)
    })
  }

}

module.exports = TodoDao
```
ではindex.htmlにアクセスしてブラウザで動作確認してみます。
![wsd-min.gif](https://qiita-image-store.s3.amazonaws.com/0/169092/361b022c-9f3c-6cc5-5824-1301e8a4bdf8.gif)
DBなので、ブラウザの別オブジェクトからも同じデータが参照できているのがわかりますね。当然ブラウザを閉じてもデータが保持されます。

# 最後に
Web SQL Databaseは簡単に扱えるのですが、実はブラウザDBとしてはIndexedDBというAPIの方が推奨されています。しかし、こちらもサポートしているブラウザは多いので、使用を検討しても良いんじゃないかと思いました。