UnityのSQLite3にSQLiteUnityKit以外の選択肢～Dapper＆SqlKata編～（Windows・Android対応）


UnityへのSQLite3の導入に想像以上に時間がかかったため備忘録として記事にまとめます。

Unityで永続的なデータの保管のためにDBを利用する場合、
Sqlite3(RDB)とLiteDB(NoSQL)が検索上位に挙がりますが、本記事では、UnityへのSQLite3の導入方法をまとめます。

## ライブラリの調査

Googleで「Unity SQLite3」と検索してみたところ、SQLite-netかSQLiteUnityKitが検索上位に表示されます。（2022/04/24現在）
両ライブラリの違いを下記の表にまとめました。

| | SQLite-net | SQLiteUnityKit |
| --- | --- | --- |
| star | 3300以上 | 286 |
| fork| 1400以上 | 75 |
| 暗号化 | o (SQLCipher対応) | x |
| ORM | o | x |
|最新のコミット| 2021-09-03 | 2014-11-06 |

機能が豊富なSQlite-netを有力候補としたいところですが、
最後のコミットが8か月近く前なため今後も継続的なメンテナンス期待するには少々不安があります。

そこで、Unityで一般的であると思われる方法に限らず、C#として一般的な方法を調査したところ以下のORMがヒットしました。

| ライブラリ名 | 備考|
| --- | --- |
| Entity Framework | MS謹製 豊富な機能 |
| Dapper |シンプルな機能 Entity Frameworkより速度が速いらしい |

[C#のORM（オブジェクト関係マッピング）における理想形を考えてみる](https://qiita.com/ikuosaito1989/items/f332863dfbe5f30fdf4a)
上記記事を参考に今回はSqlKataをUnityに導入します。

## ライブラリの導入

依存関係も管理してくれる便利ツール[NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity)を利用して以下のパッケージをプロジェクトに導入します。

* Dapper
* SqlKata
* SqlKata.Execution
* Microsoft.Data.Sqlite.Core
* SQLitePCLRaw.bundle_green

*1) SqlKata.Executionの依存関係はdapper (>= 1.50.5)以上のため最新のDapperを導入している。

## DLLの導入

#### Windows

NugetForUnityではSQLitePCLRaw.lib.e_sqlite3内にあるDLLを展開してくれないため、
SQLitePCLRaw.lib.e_sqlite3.nupkgを手動で解凍して以下のパスのファイルを __Assets/Plugins/__ 以下に配置します。

```
SQLitePCLRaw.lib.e_sqlite3.2.0.7\runtimes\win-x86\native\e_sqlite3.dll
SQLitePCLRaw.lib.e_sqlite3.2.0.7\runtimes\win-x64\native\e_sqlite3.dll
```

各種DLLに対して添付画像を参考に設定します。

* 64bit版

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1891872/7df790a8-f26a-ed5c-75c8-6b6cafed51e8.png)

* 32bit版

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1891872/7fb9036b-419f-f84f-46b6-dc3c8c03459c.png)


#### Android


以下のlibe_sqlite3.soファイルは、Android用にコンパイルされたネイティブライブラリではないため、
Android実機でテストプレイを実行すると、libc.so.6を参照できないためDLLNotFoundExceptionがスローされます。

> SQLitePCLRaw.lib.e_sqlite3.2.0.7\runtimes\linux-arm\native\libe_sqlite3.so
> SQLitePCLRaw.lib.e_sqlite3.2.0.7\runtimes\linux-arm64\native\libe_sqlite3.so

したがって、Android用のlibe_sqlite3.soを取得するため、
公式のリポジトリのリンクよりSQLitePCLRaw.lib.e_sqlite3で配布するネイティブライブラリを管理しているリポジトリをクローンします。

https://github.com/ericsink/cb

以下のパスのフォルダを __Assets/Plugins/Android/__ 以下に配置します。

```
cb\bld\bin\e_sqlite3\android\arm64-v8a
cb\bld\bin\e_sqlite3\android\armeabi-v7a
cb\bld\bin\e_sqlite3\android\x64
cb\bld\bin\e_sqlite3\android\x86
```

## 接続確認

### DB

適当なDBファイルを作成して __Assets/StreamingAssets/__ 以下に配置します。

### スクリプト

以下のようなスクリプトを作成して適当なGameObjectにアタッチします。
プラットフォームごとにログが出力されれば導入完了です。

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.Data.Sqlite;
using SqlKata.Execution;
using SqlKata.Compilers;
using UnityEngine.Networking;

/// <summary>
/// SQLite3の接続確認クラス
/// </summary>
public class SqliteConnectionTester : MonoBehaviour
{
    // DB名
    static readonly string s_dbName = "database.sqlite3";
    // テーブル名
    static readonly string s_tableName = "Scenes";
    // カラム名
    static readonly string s_columnName = "Id";

    void Start()
    {
        CloneDB();

        SqliteConnectionStringBuilder builder = new SqliteConnectionStringBuilder
        {
            DataSource = System.IO.Path.Combine(GetPlatFormDataPath(), s_dbName)
        };

        SQLitePCL.Batteries_V2.Init();

        Debug.Log("接続確認");

        SqliteConnection connection = new SqliteConnection(builder.ConnectionString);

        // SQLiteのバージョン出力
        Debug.Log("バージョン情報：" + connection.ServerVersion);

        SqliteCompiler compiler = new SqliteCompiler();
        QueryFactory factory = new QueryFactory(connection, compiler);

        Debug.Log("レコード出力：" + s_tableName);
        // scenesテーブルからidカラムのデータを取得する。
        foreach (System.Object obj in factory.Query(s_tableName).Select(s_columnName).Get<System.Object>())
        {
            // テーブルからデータを取得できているか確認
            Debug.Log(obj.ToString());
        }
    }

    string GetPlatFormDataPath()
    {
#if !UNITY_EDITOR && UNITY_ANDROID
        using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        using (var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
        using (var getFilesDir = currentActivity.Call<AndroidJavaObject>("getFilesDir"))
        {
            return getFilesDir.Call<string>("getCanonicalPath");
        }
#else
        return Application.persistentDataPath;
#endif
    }

    void CloneDB()
    {
        string targetPath = System.IO.Path.Combine(GetPlatFormDataPath(), s_dbName);
        if (System.IO.File.Exists(targetPath)) return;
        string sourcePath = System.IO.Path.Combine(Application.streamingAssetsPath, s_dbName);

#if !UNITY_EDITOR && UNITY_ANDROID
        StartCoroutine(Copy(sourcePath, targetPath));
#else
        System.IO.File.Copy(sourcePath, targetPath);
#endif

        IEnumerator Copy(string sourcePath, string targetPath)
        {
            using (UnityWebRequest request = UnityWebRequest.Get(sourcePath))
            {
                yield return request.SendWebRequest();
                System.IO.File.WriteAllBytes(targetPath, request.downloadHandler.data);
            }
        }
    }
}

```

### Windows

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1891872/65e60b35-f5c4-45d6-46cc-d3894fd7bf88.png)

### Android

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1891872/fb58b6cc-ba96-1124-8516-940a3c8d0b8c.png)

## 補足

手元にiOSの実機がないため動作確認ができませんが、
iOSの場合はSQLitePCLRaw.bundle_greenでシステムのSQLiteライブラリを使用するため特別な対応は不要らしいです。
情報提供お待ちしております。

## 参照

https://github.com/praeclarum/sqlite-net

https://github.com/Busta117/SQLiteUnityKit

https://github.com/GlitchEnzo/NuGetForUnity

https://qiita.com/ikuosaito1989/items/f332863dfbe5f30fdf4a