CommentOut

【React Native+Expo】Expoでデータベース(SQLite)の使い方 【React Native+Expo】Expoでデータベース(SQLite)の使い方

【React Native+Expo】Expoでデータベース(SQLite)の使い方

公開日:  最終更新日:

スマホアプリを作る時、データを保存する必要がありますよね。
スマホではSQLiteを使う場面が多いかと思います。もちろんExpoにもSQLiteがあって、SQLiteの公式ドキュメントもあります。
が、公式ドキュメントの情報量が少ないです。
そのため、処理をまとめました。

ExpoでSQLiteを使う準備

通常通り、Expoプロジェクトを作成した上で、コマンドでexpo-sqliteをインストールします。
以下のコマンドでインストールします。

$ npx expo install expo-sqlite

ちなみに、npmでインストールされたものは、node_modules内にインストールされるので、パッと見でインストールされているのかよくわかりません。
インストールされているか確認する時は、package.jsonを確認してください。

"dependencies": {
    "expo": "~47.0.8",
    "expo-asset": "~8.6.2",
    "expo-file-system": "~15.1.1",
    "expo-sqlite": "~11.0.0",
    "expo-status-bar": "~1.4.2",
    "react": "18.1.0",
    "react-native": "0.70.5"
},

このdependenciesにインストールされたモジュールが追加されていきます。

"expo-sqlite": "~11.0.0",

expo-sqliteが入ってて、バージョンが記載されていますね。

expo-sqliteをインポートします

ExpoでSQLiteを使うために、インストールしたexpo-sqliteをインポートします。

import * as SQLite from 'expo-sqlite';

expo-sqliteでテーブルを作る場合

今回はボタンを押したタイミングでテーブルを作成します。

<Button
    title="CREATE"
    onPress={() => {
        const db = SQLite.openDatabase('DB.db');
        db.transaction((tx) => {
            // 実行したいSQL
            tx.executeSql(
                "CREATE TABLE IF NOT EXISTS HealthData(id integer primary key not null, height real, weight real);",
                null,
                () => {
                    // 成功時のコールバック
                    console.log("CREATE TABLE Success.");
                },
                () => {
                    // 失敗時のコールバック
                    console.log("CREATE TABLE Failed.");
                    return true;  // return true でロールバックする
                });
            },
            () => { console.log("Failed All."); },
            () => { console.log("Success All."); }
        );
    }} />
// データベースを開きます。
const db = SQLite.openDatabase([DB名]);

SQLite.openDatabase([DB名]);でデータベースを開きます。
ここで指定したDB名のファイルがない場合、ファイルの作成まで行ってくれます

トランザクションの実行

SQLite.openDatabase([DB名]);で代入した定数「db」には関数があります。
「db」に含まれる関数「transaction」を呼び出して、SQLを実行させます。

transaction(callback, errorCallback, successCallback);

callbackSQLを実行する関数を入れます
errorCallbackエラーが発生した時の関数を入れます
successCallback成功した時の関数を入れます。
戻り値なし

db.transactionの引数に入れるのは全て関数です。

db.transaction(
    (tx) => {~~~},
    () => { console.log("Failed All."); },
    () => { console.log("Success All."); }
);

引数の2番目、3番目はそれぞれSQLの実行に失敗した時、成功した時の処理を入れます。

最初のcallbackに何が入るかというと、SQLを実行する関数が入ります。

executeSql(sqlStatement, args, callback, errorCallback)

sqlStatementSQL文を入れます
argsSQLのプリペアドステートメントの変数
callbackSQLが上手に動いた時に実行されるコールバック
errorCallbackSQLを実行してエラーが発生した時のコールバック
戻り値なし
tx.executeSql(
    SQL文,
    null,
    () => {
        // 成功時のコールバック
    },
    () => {
        // 失敗時のコールバック
        return true;  // return true でロールバックする
    }
);

tx.executeSqlはdb.transactionの第一引数に(tx) => {~~~}を入れて、この関数の引数txを使って、executeSql関数を動かします。

処理しているSQLは以下の通りです。

CREATE TABLE IF NOT EXISTS
    HealthData (
        id integer primary key not null,
        height real,
        weight real
    );

HealthDataという健康管理用のテーブルを作成しています。
IF NOT EXISTSの部分で「テーブルが無ければ作ってね」という命令をして、既にテーブルが存在しているのに改めて同名のテーブルを作ろうとする行為を防止しています。

expo-sqliteでテーブルにレコードを追加する場合

<Button
    title="ADD"
    onPress={() => {
        const db = SQLite.openDatabase('DB.db');
        db.transaction((tx) => {
            // 実行したいSQL
            tx.executeSql(
                "INSERT INTO HealthData(id, height, weight) VALUES(?, ?, ?);",
                [1, 170.0, 60.0],
                () => {
                    // 成功時のコールバック
                    console.log("INSERT TABLE Success.");
                },
                () => {
                    // 失敗時のコールバック
                    console.log("INSERT TABLE Failed.");
                    return true;  // return true でロールバックする
                });
            },
            () => { console.log("Failed All."); },
            () => { console.log("Success All."); }
        );
    }} />

使っている関数や処理の流れはテーブル作った時と同じです。
SQL文の部分が違うだけですね。

SQLはこんな感じ

INSERT INTO
    HealthData(
        id,
        height,
        weight
    )
    VALUES(
        ?,  -- id
        ?,  -- height
        ?   -- weight
    );

今回、テーブルを作った時とは違い、データを挿入するため、プリペアドステートメントを使っています。
それがSQL内の?マークの部分ですね。
この?マークの部分は次の引数のargsの値が順番に挿入されます。
このサンプルでは[1, 170.0, 60.0]の3つの値が順番に挿入され、最終的に以下のような文章として扱われます。

INSERT INTO
    HealthData(
        id,
        height,
        weight
    )
    VALUES(
        1,     -- id
        170.0, -- height
        60.0   -- weight
    );

expo-sqliteでレコードを取得する場合

<Button
    title="SHOW"
    onPress={() => {
        const db = SQLite.openDatabase('DB.db');
        db.transaction((tx) => {
            // 実行したいSQL
            tx.executeSql(
                "SELECT * FROM Health;",
                [],
                (_, resultSet) => {
                    // 成功時のコールバック
                    setItems(resultSet.rows._array);
                    console.log(`件数:${items.length}件`);
                    for (let i=0; i<items.length; i++) {
                        const id = items[i].id;
                        const height = items[i].height;
                        const weight = items[i].weight;
                        console.log(`${id}:${height}:${weight}`);
                    }
                },
                () => {
                    // 失敗時のコールバック
                    console.log("SELECT TABLE Failed.");
                    return false;  // return true でロールバックする
                });
            },
            () => { console.log("Failed All."); },
            () => { console.log("Success All."); }
        );
    }} />

データを取得する際もほとんど一緒ですが、取得する場合、データを受け取らないといけないので、その部分が違います。
成功時のコールバックの引数が()から(_, resultSet)に変化しています。

SELECTで取得されたデータは「resultSet」に入ります
resultSet.rows._arrayの中に配列の形で取得したデータが入ります。

今回はuseStateに一時的に保存しています。

expo-sqliteでデータを削除する場合

<Button
    title="DELETE"
    onPress={() => {
        const db = SQLite.openDatabase('DB.db');
        db.transaction((tx) => {
            // 実行したいSQL
            tx.executeSql("delete from players;",
                null,
                () => {
                    // 成功時のコールバック
                    console.log("DELETE TABLE Success.");
                },
                () => {
                    // 失敗時のコールバック
                    console.log("DELETE TABLE Failed.");
                    return true;  // return true でロールバックする
                });
            },
            () => { console.log("Failed All."); },
            () => { console.log("Success All."); }
        );
    }} />

DELETEに関しては、今まで出てきた物を使ってやってます。
条件で絞り込んで削除する場合はWHEREで値はプリペアドステートメントを使いましょう。

宣伝
WordPressサイトのテンプレート編集やトラブル対応、バグ修正、簡単なJavascriptの作成(カルーセルやバリデーション等)など、小規模なスポット対応を受け付けております。
もしお困りごとがありましたら、お問い合わせフォームよりご相談ください。

この記事を書いた人

uilou

uilou

プログラマー

基本的に、自分自身の備忘録のつもりでブログを書いています。 自分と同じ所で詰まった人の助けになれば良いかなと思います。 システムのリファクタリングを得意としており、バックエンド、フロントエンド、アプリケーション、SQLなど幅広い知識と経験があります。 広いだけでなく、知識をもっと深堀りしていきたいですね。