為什么我不再使用Realm
也許你并沒有聽說過 Realm ,這是一個面向安卓(亦面相iOS)的移動端數據庫技術。和SQLite不同,它允許你在持久層直接和數據對象工作。在它之上是一個函數式風格的查詢api,眾多的努力讓它比傳統的SQLite 操作更快 。基于這些原因讓我決定試試Realm 。
大約一年前,當我第一次使用Realm 的時候,給我的第一印象非常不錯。我需要保持一些用戶數據在手機本地,但是SharedPreferences用起來有點復雜了。Realm允許我用快速干凈的代碼里完成這件事情。它完全不需要像SQLite那樣自己手動寫額外的代碼。
我接下來的一個項目在缺少網絡連接的時候需要一個比較復雜的離線模式。從網絡抓取的數據必須保存在手機本地。我決定完全使用Realm 并觀察它隨項目增大是如何擴大的。
而我很快發現Realm讓數據模型的工作成了一種負擔。它有幾個限制以至于我必須在整個代碼基礎上做處理。結果我嘗試在Realm之上抽象出另外一層來減輕這種限制。
定義對象
為了說明這些限制,讓我們從簡單的Person對象開始:
public class Person extends RealmObject { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
注意我們必須直接繼承自RealmObject。這阻礙我們利用數據模型中的任意類型的繼承。
并且我們還不能定義除setters 和 getters之外的實例方法。如果你想重寫equals 或者toString`那么你就別想了。這樣導致的另外一個后果就是我們只能局限于使用標記接口模式(marker interfaces) (注解也是可以的 )。
不僅僅被限制于setters 和 getters,實際上我們還必須提供它們。因此我們的數據對象是不可變的!另外,setters 和 getters方法只是為Realm替換自己實現的代理方法。它不能操作數據,跑出異常,或者打印日志。
雖然我們可以提供一個非默認的構造函數,但是我們必須保證存在一個空的構造函數。如果你想用一個builder 或者工廠方法來作為實例化的唯一途徑,那么這種限制就成了一個問題。稍后我們將看看如何用Realm創建對象。
在我們能持有的field類型方面,也有一些限制。所有的基本數據類型以及它們的封裝類型都能支持,包括String, Date, 和byte[]`。但是對于其它類型,為了被持久化,必須繼承自RealmObject。Lists可以用RealmList來支持。
但是也僅此而已。如果我們想使用枚舉而不是int,是沒有辦法的(找到一個使用@IntDef的理由了)。我們還不能使用集合類型,比如Set和Map。
創建和更新對象
為了創建一個Person類的實例,我們必須做如下事情:
Realm realm = Realm.getInstance(context); realm.beginTransaction(); Person person = realm.createObject(Person.class); person.setName("John"); person.setAge(25); realm.commitTransaction();
你會注意到我們必須包裹一下Person 對象,同時任何對它的修改都在一個transaction 中。如果我們能在transaction 之外做這件事情并在我們準備好的時候持久化它,就要靈活得多。而現在我們在想要創建或者更新我們對象的任何時候都要卡在寫額外的Realm 代碼上面。
之前我提過我們可以定義一個非默認的構造函數。比如,對于Person我們可能有一個帶有name 和 age的構造函數:
public class Person extends RealmObject { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { // required empty public constructor } // setters and getters ... }
我們不再需要直接調用setters :
Person person = new Person("John", 25); realm.beginTransaction(); Person realmPerson = realm.copyToRealm(person); realm.commitTransaction();
這讓我們省去了一些寫額外代碼的時間,但是仍然受限于transaction。
Mitigating These Issues
為了避免在基礎代碼中處理這些限制,我為數據對象定義了兩套類: POJOs (普通對象)和Realm 對象。然后我們創建了一個能在兩者之間映射的abstraction 。
這是可行的,但是有兩個主要的問題。第一個是當你持有許多不同類型的對象時,你需要許多代碼來映射這些類。管理這些是很痛苦的而且這也很容易產生bug。 對象映射的概念以及它存在的問題都不是什么新東西了。
第二個就是我覺得這有違最初使用Realm的目的。能在持久層直接使用對象是它的主要好處。如果我們為了使用POJO而必須在Realm 之上創建抽象,那么它相比SQLite或者像 DBFlow一樣的ORM的優勢在哪里呢?
值得一提的是Realm 的維護者已經 意識到了這些限制 ,而且在一定程度上,許多問題都可能被解決(見這里 和 這里))。Realm也的確具有一些其它的優勢,比如性能以及在iOS和安卓之間共享數據的能力。
英文原文:Why I Don't Use Realm Anymore
來自: http://www.jcodecraeer.com//a/anzhuokaifa/androidkaifa/2015/1203/3743.html