Realm 是最好的 Android 數據庫解決方案
已發表: 2022-03-11自從 Android 創建以來,我們的應用程序開發人員一直在使用 SQLite 來存儲我們的本地數據。 有時直接使用 SQL 語句,有時使用對象關係映射器 (ORM) 作為抽象層,但無論哪種方式,我們最終都在使用 SQLite。
儘管有 SQLite 的所有優點,但有時我們希望我們有替代關係模型的方法:可以讓我們不必添加樣板代碼來將值與數據庫進行轉換,或者使我們能夠跳過設置映射在類和表、字段和列、外鍵等之間。
換句話說,一個數據結構與我們在應用程序級別實際使用的數據結構更相似的數據庫。 更好的是,如果它可以通過設計提高內存效率,從而在資源受限的設備中提供更好的體驗,那就太棒了。
事實上,這些是我們通過 Realm 獲得的一些開箱即用的好處,Realm 是一個具有獨特架構的數據庫平台,已成為 SQLite 的新替代品。
本文介紹了 Realm 引起如此多關注的一些主要原因,以及為什麼您可能要考慮嘗試它。 它討論了 Realm 相對於 SQLite 為 Android 開發人員提供的一些關鍵優勢。
由於 Realm 可在多個平台上使用,因此本文將介紹的一些內容也與其他移動平台相關,例如 iOS、Xamarin 和 React Native。
SQLite:它有效,但它不是你大多數時候需要的
大多數移動開發人員可能都熟悉 SQLite。 它自 2000 年以來一直存在,可以說是世界上使用最多的關係數據庫引擎。
SQLite 有許多我們都承認的好處,其中之一就是它對 Android 的原生支持。
它是一個標準的 SQL 關係數據庫這一事實也最大限度地減少了那些來自關係數據庫背景的人的學習曲線。 如果充分發揮其潛力(利用功能,例如準備好的語句、帶有事務的批量操作等),它還可以提供相當好的性能。 儘管 SQLite 可能無法很好地滿足您的所有需求。
但是,直接處理 SQL 語句有許多缺點。
根據官方 Android 文檔,以下是開始讀取/寫入 SQLite 所需的步驟:
- 用合同類來描述你的模式。
- 在字符串中定義您的創建/刪除表命令。
- 擴展
SQLiteOpenHelper
以運行創建命令和管理升級/降級。
完成此操作後,您就可以讀取和寫入數據庫了。 但是,您需要在應用程序中的對象和數據庫中的值之間來迴轉換。 長話短說:這是很多樣板代碼!
另一個問題是可維護性。 隨著您的項目變得越來越大並且需要編寫更複雜的查詢,您最終會得到大量字符串形式的原始 SQL 查詢。 如果以後您需要更改這些查詢的邏輯,那可能會很麻煩。
儘管有缺點,但在某些情況下使用原始 SQL 是您的最佳選擇。 一個例子是,當您開發一個庫時,性能和大小是關鍵因素,如果可能,應避免添加第三方庫。
對象關係映射器:SQL 挑戰的創可貼
為了讓我們免於處理原始 SQL,ORM 來拯救我們。
一些最著名的 Android ORM 是 DBFlow、greenDAO 和 OrmLite。
它們帶來的最大價值是 SQLite 抽象,讓我們相對容易地將數據庫實體映射到 Java 對象。
除其他好處外,應用程序開發人員可以使用對象,這是一種更熟悉的數據結構。 它還有助於可維護性,因為我們現在正在處理具有更強類型的高級對象並將臟工作留給庫。 通過連接字符串或手動處理與數據庫的連接來減少構建查詢的難度。 更少的錯別字。
儘管這些 ORM 提高了 Android 數據庫的標準是事實,但它們也有其缺點。 在許多情況下,您最終會加載不必要的數據。
這是一個例子。
假設您有一個包含 15 列的表格,並且在您的應用程序的某個屏幕中,會顯示該表格中的對象列表。 此列表僅顯示來自三列的值。 因此,通過加載表格行中的所有數據,您最終會帶來比該屏幕實際需要的數據多五倍的數據。
說實話,在其中一些庫中,您可以指定要預先檢索哪些列,但為此您需要添加更多代碼,即使如此,如果您只能確切知道您將要檢索哪些列,這還不夠在查看數據本身後使用:無論如何,某些數據可能會被不必要地加載。
此外,在某些情況下,您經常需要進行複雜的查詢,而您的 ORM 庫只是沒有為您提供使用其 API 描述這些查詢的方法。 例如,這會使您編寫效率低下的查詢,這些查詢執行的計算量超出了您的需要。
結果是性能損失,導致您求助於原始 SQL。 雖然這對我們中的許多人來說不是一個交易破壞者,但它損害了對象關係映射的主要目的,並使我們回到了上述關於 SQLite 的一些問題。
境界:完美的選擇
Realm Mobile Database 是一個專為移動設備設計的數據庫。
Realm 和 ORM 之間的主要區別在於,Realm 不是建立在 SQLite 之上的抽象,而是一個全新的數據庫引擎。 它不是基於關係模型,而是基於對象存儲。 它的核心由一個獨立的 C++ 庫組成。 它目前支持 Android、iOS(Objective-C 和 Swift)、Xamarin 和 React Native。
Realm 於 2014 年 6 月推出,因此目前已有兩年半的歷史(相當新!)。
雖然服務器數據庫技術自 2007 年以來正在經歷一場革命,並且出現了許多新的技術,但用於移動設備的數據庫技術仍然停留在 SQLite 及其包裝器上。 這是從頭開始創建東西的關鍵動機之一。 此外,正如我們將看到的,Realm 的一些特性需要對數據庫在低級別的行為方式進行根本性的改變,而這在 SQLite 之上構建是不可能的。
但是Realm真的值得嗎? 以下是您應該考慮將 Realm 添加到工具帶的主要原因。
輕鬆建模
以下是使用 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
)、 String
、 Date
和byte[]
。 它還支持RealmObject
和RealmList<? extends RealmObject>
RealmList<? extends RealmObject>
為模型關係。
字段可以具有任何訪問級別(私有、公共、受保護等)。 默認情況下,所有字段都是持久化的,您只需要註釋“特殊”字段(例如, @PrimaryKey
用於您的主鍵字段, @Ignore
用於設置非持久化字段等)。
這種方法的有趣之處在於,與 ORM 相比,它使類的“註釋污染”更少,因為在大多數情況下,您需要註釋來將類映射到表、將常規字段映射到數據庫列、將外鍵字段映射到其他表等等在。
關係
當談到關係時,有兩種選擇:
添加一個模型作為另一個模型的字段。 在我們的示例中,
Contact
類包含一個Address
字段並定義了它們之間的關係。 一個聯繫人可能有一個地址,但沒有什麼能阻止這個地址被添加到其他聯繫人中。 這允許一對一和一對多的關係。添加被引用模型的
RealmList
。RealmLists
行為很像老式的 JavaLists
,充當 Realm 對象的容器。 我們可以看到我們的Contact
模型有一個RealmList
聯繫人,在這個例子中是她的朋友。 可以使用這種方法對一對多和多對多關係進行建模。
我喜歡這種表示關係的方式,因為它對我們 Java 開發人員來說非常自然。 通過將這些對象(或這些對象的列表)直接添加為我們類的字段,就像我們對其他非模型類所做的那樣,我們不需要處理外鍵的 SQLite 設置。
警告:不支持模型繼承。 當前的解決方法是使用組合。 因此,例如,如果您有一個Animal
模型並希望創建一個從Animal
擴展的Dog
模型,您將不得不在Dog
中添加一個Animal
實例作為字段。 關於組合與繼承的爭論很大。 如果您喜歡使用繼承,那麼這絕對是您需要了解的有關 Realm 的知識。 使用 SQLite,這可以使用通過外鍵連接的兩個表(一個用於父表,一個用於子表)來實現。 一些 ORM 也沒有施加此限制,例如 DBFlow。
只檢索您需要的數據! 零拷貝設計
這是一個殺手級功能。
Realm 應用了零拷貝設計的概念,這意味著數據永遠不會被複製到內存中。 您從查詢中獲得的結果實際上只是指向真實數據的指針。 數據本身在您訪問時會延遲加載。
例如,您有一個包含 10 個字段(SQL 中的列)的模型。 如果您查詢此模型的對像以將它們顯示在屏幕上,並且您只需要 10 個字段中的三個來填充列表項,那麼這些字段將是唯一檢索到的字段。
因此,查詢速度非常快(有關一些基準測試結果,請參見此處和此處)。
與通常預先從選定 SQL 行加載所有數據的 ORM 相比,這是一個很大的優勢。
結果,屏幕加載變得更加高效,無需開發人員進一步努力:這只是 Realm 的默認行為。
此外,這也意味著應用程序消耗更少的內存,並且考慮到我們談論的是移動設備等資源受限的環境,這可能會產生很大的不同。
零拷貝方法的另一個結果是 Realm 管理的對像是自動更新的。
數據永遠不會復製到內存中。 如果您有來自查詢的結果,並且另一個線程在您查詢後更新了數據庫上的此數據,那麼您擁有的結果將已經反映了這些更改。 您的結果只是指向實際數據的指針。 因此,當您從字段訪問值時,將返回最新的數據。
例如,如果您已經從 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,這限制了它們能走多遠。 相比之下,Realm 不僅僅是另一個 SQLite 包裝器。 它可以自由地提供 ORM 無法提供的功能。
Realm 的基本變化之一是能夠將數據存儲為對像圖存儲。
這意味著從編程語言級別到數據庫,領域一直是對象。 因此,與關係數據庫相比,在寫入和讀取值時來回進行的轉換要少得多。
數據庫結構更緊密地反映了應用程序開發人員使用的數據結構。 事實上,這是服務器端開發從關係建模轉向聚合模型的主要原因之一。 Realm 最終將其中一些想法帶入了移動開發領域。
如果我們考慮 Realm 架構中的組件,最底層是它的核心,以及平台最基本的實現。 最重要的是,我們將為每個受支持的平台提供綁定庫。
當對某些您無法控制的技術使用包裝器時,您最終需要圍繞它提供某種抽象層。
領域綁定庫被設計為盡可能薄,以減少抽象的複雜性。 他們主要從 Core 傳播設計理念。 通過控制整個架構,這些組件可以更好地相互同步。
一個實際示例是訪問其他引用對象(SQL 中的外鍵)。 Realm 的文件結構基於本地鏈接,因此當您查詢關係時,不必將 ORM 抽象轉換為關係和/或連接多個表,您可以在文件格式的文件系統級別獲得指向對象的原始鏈接。
那是直接指向其他對象的對象。 因此,查詢關係與查詢整數列是一樣的。 不需要遍歷外鍵的昂貴操作。 這都是關於遵循指針的。
社區與支持
Realm 正在積極開發中,並且經常發布更新版本。
Realm 移動數據庫中的所有組件都是開源的。 他們對問題跟踪器和 Stack Overflow 的響應非常迅速,因此您可以期待在這些渠道上獲得良好和快速的支持。
此外,在確定問題(錯誤、改進、功能請求等)的優先級時,也會考慮來自社區的反饋。 很高興知道您可以在您使用的工具的開發中擁有發言權。
我從 2015 年開始使用 Realm,從那時起,我在網上看到了幾篇關於 Realm 的不同意見的帖子。 我們將很快討論它的局限性,但我注意到的一件事是,在發布帖子時提出的許多投訴已經得到解決。
例如,當我了解 Realm 時,尚不支持模型上的自定義方法和異步調用。 這些在當時對許多人來說都是破壞交易的,但目前兩者都得到支持。
這樣的開發速度和響應能力讓我們更有信心,我們不會等待很長時間來等待重要的功能。
限制
與生活中的一切一樣,Realm 並不全是玫瑰。 除了前面提到的繼承限制之外,還有其他一些缺點需要牢記:
雖然可以有多個線程同時讀取和寫入數據庫,但Realm 對像不能跨線程移動。 因此,例如,如果您使用在後台線程中運行的 AsyncTask 的
doInBackground()
檢索領域對象,則不能將此實例傳遞給onPostExecute()
方法,因為它們在主線程上運行。 這種情況的可能解決方法是製作對象的副本並將其傳遞或傳遞對象的 id 並在onPostExecute()
上再次檢索對象。 Realm 提供了用於讀/寫的同步和異步方法。不支持自動增量主鍵,因此您需要自己處理這些的生成。
不可能同時從不同的進程訪問數據庫。 根據他們的文檔,多進程支持即將推出。
Realm 是移動數據庫解決方案的未來
SQLite 是一個可靠、健壯且經過驗證的數據庫引擎,並且關係數據庫不會很快消失。 那裡有許多好的 ORM,它們也可以在許多場景中發揮作用。
但是,重要的是要及時了解當前趨勢。
在這方面,我認為 Realm 是近年來移動數據庫開發的最大趨勢之一。
Realm 帶來了一種獨特的方法來處理對開發人員有價值的數據,不僅因為它可以比現有解決方案更好的選擇,還因為它在新的可能性方面拓寬了我們的視野並提高了移動數據庫技術的標準。
你已經有使用 Realm 的經驗了嗎? 請隨時分享您的想法。