レルムは最高のAndroidデータベースソリューションです

公開: 2022-03-11

Androidが作成されて以来、アプリ開発者はSQLiteを使用してローカルデータを保存してきました。 SQLステートメントを直接使用する場合もあれば、オブジェクトリレーショナルマッパー(ORM)を抽象化レイヤーとして使用する場合もありますが、いずれにしても、1日の終わりにSQLiteを使用してきました。

SQLiteのすべての利点にもかかわらず、リレーショナルモデルの代替手段が必要な場合がありました。データベースとの間で値を変換するための定型コードを追加したり、マッピングの設定をスキップしたりする必要がないものです。クラスとテーブル、フィールドと列、外部キーなどの間。

つまり、アプリケーションレベルで実際に使用しているものに似たデータ構造を持つデータベースです。 さらに良いことに、それが設計によってメモリ効率が高く、リソースに制約のあるデバイスでより良いエクスペリエンスを可能にすることができれば、それは素晴らしいことです。

実際、これらは、SQLiteの新しい代替手段として登場した、独自のアーキテクチャを備えたデータベースプラットフォームであるRealmで得られるすぐに使える利点の一部です。

この記事では、Realmが非常に注目を集めている主な理由と、試してみることを検討する理由をいくつか紹介します。 RealmがSQLiteよりもAndroid開発者に提供する主な利点のいくつかについて説明します。

Realmは複数のプラットフォームで利用できるため、この記事で取り上げる内容の一部は、iOS、Xamarin、ReactNativeなどの他のモバイルプラットフォームにも関連しています。

SQLite:動作しますが、ほとんどの場合必要なものではありません

ほとんどのモバイル開発者はおそらくSQLiteに精通しています。 それは2000年以来存在しており、間違いなく世界で最も使用されているリレーショナルデータベースエンジンです。

SQLiteには、私たち全員が認めている多くの利点があります。その1つは、Androidでのネイティブサポートです。

これが標準のSQLリレーショナルデータベースであるという事実は、リレーショナルデータベースのバックグラウンドから来た人々の学習曲線も最小限に抑えます。 また、その潜在能力を最大限に活用すると、適度に優れたパフォーマンスを提供します(プリペアドステートメント、トランザクションを使用した一括操作などの機能を活用します)。 SQLiteは、すべてのニーズに対応できるとは限りませんが。

ただし、SQLステートメントを直接処理することには、いくつかの欠点があります。

Androidの公式ドキュメントによると、SQLiteの読み取り/書き込みを開始するために必要な手順は次のとおりです。

  1. コントラクトクラスの観点からスキーマを説明します。
  2. テーブルの作成/削除コマンドを文字列で定義します。
  3. SQLiteOpenHelperを拡張して、作成コマンドを実行し、アップグレード/ダウングレードを管理します。

これを実行すると、データベースの読み取りと書き込みの準備が整います。 ただし、アプリケーション内のオブジェクトとデータベース内の値の間を行ったり来たりする必要があります。 簡単に言うと、ボイラープレートコードがたくさんあります。

もう1つの問題は保守性です。 プロジェクトが大きくなり、より複雑なクエリを作成する必要が生じると、文字列に生のSQLクエリの大きな塊ができてしまいます。 後でそれらのクエリのロジックを変更する必要がある場合、それは非常に面倒な場合があります。

その欠点にもかかわらず、生のSQLを使用することが最良の選択肢である場合があります。 1つの例は、パフォーマンスとサイズが重要な要素であるライブラリを開発している場合であり、可能であればサードパーティのライブラリを追加することは避けてください。

オブジェクトリレーショナルマッパー:SQLチャレンジのバンドエイド

生のSQLを扱うことから私たちを救うために、ORMが助けになりました。

最も有名なAndroidORMには、DBFlow、greenDAO、OrmLiteがあります。

それらがもたらす最大の価値はSQLiteの抽象化であり、データベースエンティティをJavaオブジェクトに比較的簡単にマッピングできます。

他の利点の中でも、アプリケーション開発者は、はるかに馴染みのあるデータ構造であるオブジェクトを操作できるようになります。 また、より強力なタイピングで高レベルのオブジェクトを処理し、ダーティな作業をライブラリに任せるため、保守性にも役立ちます。 文字列を連結したり、データベースとの接続を手動で処理したりすることで、クエリの作成に苦労することが少なくなります。 タイプミスが少なくなります。

これらのORMがAndroidデータベースの水準を引き上げたことは事実ですが、欠点もあります。 多くの場合、不要なデータをロードすることになります。

これが例です。

15列のテーブルがあり、アプリの特定の画面に、このテーブルのオブジェクトのリストが表示されているとします。 このリストには、3つの列の値のみが表示されます。 したがって、テーブルの行からすべてのデータをロードすると、その画面に実際に必要なデータの5倍のデータがもたらされることになります。

正直なところ、これらのライブラリの一部では、事前に取得する列を指定できますが、そのためにはさらにコードを追加する必要があります。それでも、正確にどの列を取得するかしかわからない場合は、それだけでは不十分です。データ自体を確認してから使用してください。いずれにせよ、一部のデータが不必要にロードされる可能性があります。

さらに、複雑なクエリを作成するシナリオがよくあり、ORMライブラリはAPIを使用してこれらのクエリを記述する方法を提供していません。 たとえば、必要以上の計算を行う非効率的なクエリを作成する可能性があります。

その結果、パフォーマンスが低下し、生のSQLに頼ることになります。 これは私たちの多くにとって取引を妨げるものではありませんが、オブジェクトリレーショナルマッピングの主な目的を損ない、SQLiteに関する前述の問題のいくつかに戻ります。

レルム:完璧な代替手段

Realm Mobile Databaseは、モバイルデバイス向けにゼロから設計されたデータベースです。

RealmとORMの主な違いは、RealmはSQLiteの上に構築された抽象化ではなく、まったく新しいデータベースエンジンであるということです。 リレーショナルモデルではなく、オブジェクトストアに基づいています。 そのコアは、自己完結型のC++ライブラリで構成されています。 現在、Android、iOS(Objective-CおよびSwift)、Xamarin、およびReactNativeをサポートしています。

レルムは2014年6月にリリースされたため、現在2年半前です(かなり新しいです!)。

サーバーデータベーステクノロジーは2007年以来革命を遂げており、多くの新しいテクノロジーが登場していますが、モバイルデバイス用のデータベーステクノロジーはSQLiteとそのラッパーに固執したままでした。 これは、ゼロから何かを作成する主な動機の1つでした。 さらに、これから説明するように、Realmの機能の一部では、データベースの低レベルでの動作に根本的な変更が必要でしたが、SQLiteの上に何かを構築することは不可能でした。

しかし、レルムは本当に価値がありますか? ツールベルトにレルムを追加することを検討する必要がある主な理由は次のとおりです。

簡単なモデリング

Realmで作成されたいくつかのモデルの例を次に示します。

 public class Contact extends RealmObject { @PrimaryKey String id; protected String name; String email; @Ignore public int sessionId; //Relationships private Address address; private RealmList<Contact> friends; //getters & setter left out for brevity }
 public class Address extends RealmObject { @PrimaryKey public Long id; public String name; public String address; public String city; public String state; public long phone; }

モデルはRealmObjectから拡張されます。 Realmは、すべてのプリミティブ型とそのボックス型( charを除く)、 StringDate 、およびbyte[]を受け入れます。 RealmObjectおよびRealmList<? extends RealmObject>のサブクラスもサポートします。 RealmList<? extends RealmObject>をモデル関係に拡張します。

フィールドには、任意のアクセスレベル(プライベート、パブリック、保護など)を設定できます。 すべてのフィールドはデフォルトで永続化され、「特別な」フィールドに注釈を付ける必要があります(たとえば、主キーフィールドの@PrimaryKey 、非永続フィールドの設定の@Ignoreなど)。

このアプローチの興味深い点は、ORMと比較して、クラスの「注釈の汚染」が少ないことです。ほとんどの場合、クラスをテーブルにマップするための注釈、通常のフィールドをデータベース列にマップするための注釈、外部キーフィールドを他のテーブルにマップするための注釈が必要です。オン。

関係

関係に関しては、2つのオプションがあります。

  • 別のモデルのフィールドとしてモデルを追加します。 この例では、 ContactクラスにAddressフィールドが含まれており、それらの関係を定義しています。 連絡先にアドレスがある場合がありますが、この同じアドレスが他の連絡先に追加されるのを妨げるものはありません。 これにより、1対1および1対多の関係が可能になります。

  • 参照されているモデルのRealmListを追加します。 RealmListsは、古き良きJava Listsのように動作し、Realmオブジェクトのコンテナとして機能します。 Contactモデルには、この例では彼女の友達である連絡先のRealmListがあることがわかります。 このアプローチでは、1対多および多対多の関係をモデル化できます。

私たちJava開発者にとって非常に自然に感じるので、この関係を表す方法が好きです。 他の非モデルクラスの場合と同様に、これらのオブジェクト(またはこれらのオブジェクトのリスト)をクラスのフィールドとして直接追加することにより、外部キーのSQLite設定を処理する必要がなくなります。

警告:モデルの継承はサポートされていません。 現在の回避策は、コンポジションを使用することです。 したがって、たとえば、 Animalモデルがあり、 Animalから拡張するDogモデルを作成したい場合は、代わりに、 DogのフィールドとしてAnimalインスタンスを追加する必要があります。 構成と継承については大きな議論があります。 継承を使用することに興味がある場合、これは間違いなくレルムについて知っておく必要があることです。 SQLiteでは、これは外部キーで接続された2つのテーブル(1つは親用、もう1つは子用)を使用して実装できます。 DBFlowのように、一部のORMもこの制限を課しません。

必要なデータのみを取得してください! ゼロコピーデザイン

これはキラー機能です。

レルムはゼロコピー設計の概念を適用します。これは、データがメモリにコピーされないことを意味します。 クエリから得られる結果は、実際には実際のデータへの単なるポインタです。 データにアクセスすると、データ自体が遅延ロードされます。

たとえば、10個のフィールド(SQLの列)を持つモデルがあります。 このモデルのオブジェクトをクエリして画面に一覧表示し、リスト項目に入力するために10個のフィールドのうち3個が必要な場合、取得されるフィールドはそれらだけになります。

結果として、クエリは非常に高速です(ベンチマーク結果については、こことここを参照してください)。

これは、通常、選択したSQL行からすべてのデータを事前にロードするORMに比べて大きな利点です。

その結果、開発者の努力を必要とせずに、画面の読み込みが大幅に効率的になります。これは、Realmのデフォルトの動作です。

さらに、これはアプリがより少ないメモリを消費することも意味し、モバイルデバイスなどのリソースに制約のある環境について話していることを考えると、大きな違いを生む可能性があります。

ゼロコピーアプローチのもう1つの結果は、レルムによって管理されるオブジェクトが自動更新されることです。

データがメモリにコピーされることはありません。 クエリの結果があり、クエリ後に別のスレッドがデータベース上のこのデータを更新した場合、所有する結果にはすでにこれらの変更が反映されています。 結果は、実際のデータへのポインタにすぎません。 したがって、フィールドから値にアクセスすると、最新のデータが返されます。

図:複数のオブジェクトおよびスレッドからのレルムデータへのアクセス。

たとえば、Realmオブジェクトからデータを既に読み取り、それらを画面に表示していて、基になるデータが変更されたときの更新を受け取りたい場合は、リスナーを追加できます。

 final RealmResults<Contact> johns = realm.where(Contact.class).beginsWith("name", "John ").findAll(); johns.addChangeListener(new RealmChangeListener<RealmResults<Contact>>() { @Override public void onChange(RealmResults<Contact> results) { // UPDATE UI } });

単なるラッパーではありません

ORMには数十のオプションがありますが、それらはラッパーであり、すべてはその下にあるSQLiteに帰着します。これにより、ORMが取得できる範囲が制限されます。 対照的に、レルムは単なるSQLiteラッパーではありません。 ORMでは提供できない機能を自由に提供できます。

Realmの基本的な変更の1つは、データをオブジェクトグラフストアとして保存できるようにすることです。

これは、レルムがプログラミング言語レベルからデータベースに至るまでのオブジェクトであることを意味します。 その結果、リレーショナルデータベースと比較して、値の書き込みと読み取りを行ったり来たりするときに行われる変換がはるかに少なくなります。

データベース構造は、アプリケーション開発者が使用するデータ構造をより厳密に反映しています。 実際、これが、サーバー側の開発でリレーショナルモデリングから集約モデルへの移行が進んでいる主な理由の1つです。 Realmはついに、これらのアイデアのいくつかをモバイル開発の世界にもたらしました。

Realmのアーキテクチャのコンポーネントについて考えると、下部には、プラットフォームの最も基本的な実装を備えたコアがあります。 その上、サポートされている各プラットフォームへのバインディングライブラリがあります。

図:コアからさまざまなライブラリまでのレルムアーキテクチャ。

制御できないテクノロジーのラッパーを使用する場合、最終的にはその周りに何らかの抽象化レイヤーを提供する必要があります。

レルムバインディングライブラリは、抽象化の複雑さを排除するために、可能な限り薄くなるように設計されています。 それらは主にコアからの設計アイデアを広めます。 アーキテクチャ全体を制御することにより、これらのコンポーネントは相互に同期して機能します。

1つの実用的な例は、他の参照オブジェクト(SQLの外部キー)へのアクセスです。 レルムのファイル構造はネイティブリンクに基づいているため、ORM抽象化をリレーショナルに変換したり、複数のテーブルを結合したりするのではなく、リレーションシップをクエリすると、ファイルシステムレベルでオブジェクトへの生のリンクがファイル形式で取得されます。

これは、他のオブジェクトを直接指しているオブジェクトです。 したがって、関係のクエリは、たとえば整数列のクエリと同じです。 外部キーを通過する高価な操作は必要ありません。 それはすべて、ポインターに従うことです。

コミュニティとサポート

レルムは活発に開発されており、更新されたバージョンをかなり頻繁にリリースしています。

RealmMobileDatabaseのすべてのコンポーネントはオープンソースです。 彼らは問題追跡システムとスタックオーバーフローに非常に敏感なので、これらのチャネルでの優れた高速サポートを期待できます。

また、問題(バグ、改善、機能要求など)に優先順位を付けるときに、コミュニティからのフィードバックが考慮されます。 使用するツールの開発について発言権を持つことができることを知っておくのは常に良いことです。

私は2015年にレルムを使い始め、それ以来、レルムについてさまざまな意見を持ったWeb上のいくつかの投稿に出くわしました。 その制限についてはすぐに話しますが、私が気づいたことの1つは、投稿時に行われた苦情の多くがその後修正されたことです。

たとえば、レルムについて知ったとき、モデルのカスタムメソッドと非同期呼び出しはまだサポートされていませんでした。 これらは当時多くの人にとって取引ブレーカーでしたが、現在は両方ともサポートされています。

このような開発のスピードと応答性により、重要な機能を長く待つ必要がないという自信が生まれます。

制限事項

人生のすべてと同様に、レルムはすべてのバラではありません。 前述の継承の制限に加えて、留意すべき他の欠点があります。

  • 複数のスレッドがデータベースの読み取りとデータベースへの書き込みを同時に行うことは可能ですが、レルムオブジェクトをスレッド間で移動することはできません。 したがって、たとえば、バックグラウンドスレッドで実行されるAsyncTaskのdoInBackground()を使用してレルムオブジェクトを取得する場合、これらはメインスレッドで実行されるため、このインスタンスをonPostExecute()メソッドに渡すことはできません。 この状況で考えられる回避策は、オブジェクトのコピーを作成して渡すか、オブジェクトのIDを渡してonPostExecute()でオブジェクトを再度取得することです。 Realmは、読み取り/書き込み用の同期および非同期の方法を提供します。

  • 自動インクリメントの主キーはサポートされていないため、これらの生成は自分で処理する必要があります。

  • 異なるプロセスから同時にデータベースにアクセスすることはできません。 彼らのドキュメントによると、マルチプロセスサポートはまもなく登場します。

レルムはモバイルデータベースソリューションの未来です

SQLiteは堅牢で堅牢な実績のあるデータベースエンジンであり、リレーショナルデータベースはすぐになくなることはありません。 多くのシナリオでもうまくいくものがたくさんあります。

ただし、現在の傾向を常に把握しておくことが重要です。

この点で、レルムは、モバイルデータベースの開発に関して、近年の最大の今後のトレンドの1つだと思います。

Realmは、開発者にとって価値のあるデータを処理するための独自のアプローチをもたらします。これは、既存のソリューションよりも優れたオプションであるだけでなく、新しい可能性の観点から視野を広げ、モバイルデータベーステクノロジーの水準を引き上げるからです。

レルムの経験はもうありますか? お気軽にご意見をお聞かせください。