文章大綱 一、Realm介紹二、Realm實戰三、Realm官方文檔四、項目源碼下載五、參考文章 一、Realm介紹 1. 什麼是Realm Realm 是一個手機資料庫,是用來替代 SQlite 的解決方案,比 SQlite 更輕量級,速度更快,因為它有一套自己的資料庫搜索引擎,並且還具有很多現代 ...
文章大綱
一、Realm介紹
二、Realm實戰
三、Realm官方文檔
四、項目源碼下載
五、參考文章
一、Realm介紹
1. 什麼是Realm
Realm 是一個手機資料庫,是用來替代 SQlite 的解決方案,比 SQlite 更輕量級,速度更快,因為它有一套自己的資料庫搜索引擎,並且還具有很多現代資料庫的優點,支持 JSON,流式 API 調用,數據變更通知,自動數據同步,簡單身份驗證,訪問控制,事件處理,最重要的是跨平臺,目前已經支持 Java、Swift、Object - C、React - Native 等多種實現。
2. Realm優勢
易用
Ream 不是在SQLite基礎上的ORM,它有自己的數據查詢引擎。並且十分容易使用。
快速
由於它是完全重新開始開發的資料庫實現,所以它比任何的ORM速度都快很多,甚至比SLite速度都要快。
跨平臺
Realm 支持 iOS & OS X (Objective‑C & Swift) & Android。我們可以在這些平臺上共用Realm資料庫文件,並且上層邏輯可以不用任何改動的情況下實現移植。
高級
Ream支持加密,格式化查詢,易於移植,支持JSON,流式api,數據變更通知等高級特性
可視化
Realm 還提供了一個輕量級的資料庫查看工具,在Mac Appstore 可以下載“Realm Browser”這個工具,開發者可以查看資料庫當中的內容,執行簡單的插入和刪除數據的操作。(windows上還不清楚)
3. 使用要求
(1)目前不支持Android以外的Java
(2)Android Studio >= 1.5.1(不支持Eclipse)
(3)JDK version >=7.
(4)支持API 9(Android 2.3)以及之後的版本
二、Realm實戰
1. 添加依賴
在project的build中加入依賴
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "io.realm:realm-gradle-plugin:2.2.1"
}
}
image
在module中加入
apply plugin: 'realm-android'
2. 創建model
創建一個User類,需要繼承RealmObject。支持public, protected和 private的類以及方法
public class User 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來聲明 Realm 數據模型之外,還可以通過實現 RealmModel介面並添加 @RealmClass修飾符來聲明。
@RealmClass
public class User implements RealmModel {
...
}
支持的屬性
boolean, byte, short,int,long,float, double,String, Date 和,byte[], RealmObject, RealmList<? extends RealmObject>
還支持Boolean, Byte, Short, Integer, Long, Float 和 Double
提示:整數類型 short、int 和 long 都被映射到 Realm 內的相同類型(實際上為 long )
(1)@PrimaryKey——表示該欄位是主鍵
使用過資料庫的同學應該看出來了,PrimaryKey就是主鍵。使用@PrimaryKey來標註,欄位類型必須是字元串(String)或整數(byte,short,int或long)以及它們的包裝類型(Byte,Short, Integer, 或 Long)。不可以存在多個主鍵,使用字元串欄位作為主鍵意味著欄位被索引(註釋@PrimaryKey隱式地設置註釋@Index)。
@PrimaryKey
private String id;
(2)@Required——表示該欄位非空
在某些情況下,有一些屬性是不能為null的。使用@Required可用於用於強行要求其屬性不能為空,只能用於Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[] 和 Date。在其它類型屬性上使用。
(3)@Required修飾會導致編譯失敗。
提示:基本數據類型不需要使用註解 @Required,因為他們本身就不可為空。
@Required
private String name;
(4)@Ignore——表示忽略該欄位
被添加@Ignore標簽後,存儲數據時會忽略該欄位。
@Ignore
private String name;
(5)@Index——添加搜索索引
為欄位添加搜索索引,這樣會使得插入的速度變慢,數據量也變得更大。不過在查詢速度將變得更快,建議只在優化讀取性能的特定情況時添加索引。支持索引:String,byte,short,int,long,boolean和Date欄位。
註意:如果你創建Model並運行過,然後修改了Model。那麼就需要升級資料庫,否則會拋異常。升級方式後面會提到
3. 初始化Realm
重寫Application類
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
}
}
AndroidManifest.xml中配置MyApp
創建方式1
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);//初始化realm
Realm mRealm = Realm.getDefaultInstance();
}
}
這時候會創建一個叫做 default.realm的Realm文件,一般來說,這個文件位於/data/data/包名/files/。通過realm.getPath()來獲得該Realm的絕對路徑。
創建方式2
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);//初始化realm
//這時候會創建一個叫做 default.realm的Realm文件,一般來說,
// 這個文件位於/data/data/包名/files/。通過realm.getPath()來獲得該Realm的絕對路徑
RealmConfiguration config = new RealmConfiguration.Builder()
.name("myrealm.realm") //文件名
.schemaVersion(0) //版本號
.build();
Realm realm = Realm.getInstance(config);
}
創建方式3
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);//初始化realm
//創建保存在記憶體中的realm,應用關閉後就清除了
// RealmConfiguration myConfig = new RealmConfiguration.Builder(this)
// .name("myrealm.realm")
// .inMemory() .build();
}
創建方式4
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);//初始化realm
//創建加密的realm
// byte[] key = new byte[64];
// new SecureRandom().nextBytes(key);
// RealmConfiguration config = new RealmConfiguration.Builder()
// .encryptionKey(key)
// .build();
// Realm realm = Realm.getInstance(config);
}
}
總結:
RealmConfiguration支持的方法:
Builder.name : 指定資料庫的名稱。如不指定預設名為default。
Builder.schemaVersion : 指定資料庫的版本號。
Builder.encryptionKey : 指定資料庫的密鑰。
Builder.migration : 指定遷移操作的遷移類。
Builder.deleteRealmIfMigrationNeeded : 聲明版本衝突時自動刪除原資料庫。
Builder.inMemory : 聲明資料庫只在記憶體中持久化。
build : 完成配置構建。
4. 關閉Realm
記得使用完後,在onDestroy中關閉Realm
@Override
protected void onDestroy() {
super.onDestroy();
// Close the Realm instance.
realm.close();
}
5. 獲取realm對象
public class RealmHelper {
public static final String DB_NAME = "myRealm.realm";
private Realm mRealm;
public RealmHelper(Context context) {
mRealm = Realm.getDefaultInstance();
}
public Realm getRealm(){
return mRealm;
}
6. 增加數據
寫入操作需要在事務中進行,可以使用executeTransaction方法來開啟事務。
(1)使用executeTransaction方法插入數據
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User user = realm.createObject(User.class);
user.setName("Gavin");
user.setAge(23);
}
});
註意:如果在UI線程中插入過多的數據,可能會導致主線程擁塞。
(2)使用copyToRealmOrUpdate或copyToRealm方法插入數據
當Model中存在主鍵的時候,推薦使用copyToRealmOrUpdate方法插入數據。如果對象存在,就更新該對象;反之,它會創建一個新的對象。若該Model沒有主鍵,使用copyToRealm方法,否則將拋出異常。
final User user = new User();
user.setName("Jack");
user.setId("2");
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(user);
}
});
如果你用的是這樣的modle
public class User2 extends RealmObject {
public String name;
public int age;
}
就這樣寫
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User2 user = realm.createObject(User2.class);
user.name = "Micheal";
user.age = 30;
}
});
(3)上面都是用可executeTransaction方法插入數據,還有另一種方法可以用於插入數據——beginTransaction和commitTransaction
mRealm.beginTransaction();//開啟事務
User user = mRealm.createObject(User.class);
user.setName("Gavin");
user.setId("3");
mRealm.commitTransaction();//提交事務
在插入前,先調用beginTransaction(),完成後調用commitTransaction()即可。
註意:在UI和後臺線程同時開啟創建write的事務,可能會導致ANR錯誤。為了避免該問題,可以使用executeTransactionAsync來實現。
(4)使用executeTransactionAsync
該方法會開啟一個子線程來執行事務,並且在執行完成後進行結果通知。
RealmAsyncTask transaction = mRealm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User user = realm.createObject(User.class);
user.setName("Eric");
user.setId("4");
}
});
還可以加入監聽
RealmAsyncTask transaction = mRealm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User user = realm.createObject(User.class);
user.setName("Eric");
user.setId("4");
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
//成功回調
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
//失敗回調
}
});
註意:如果當Acitivity或Fragment被銷毀時,在OnSuccess或OnError中執行UI操作,將導致程式奔潰 。用RealmAsyncTask .cancel();可以取消事務在onStop中調用,避免crash
public void onStop () {
if (transaction != null && !transaction.isCancelled()) {
transaction.cancel();
}
}
(5)Realm還是個很好的功能就是將Json字元串轉化為對象
// 一個city model
public class City extends RealmObject {
private String city;
private int id;
// getters and setters left out ...
}
// 使用Json字元串插入數據
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.createObjectFromJson(City.class, "{ city: \"Copenhagen\", id: 1 }");
}
});
// 使用InputStream插入數據
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
try {
InputStream is = new FileInputStream(new File("path_to_file"));
realm.createAllFromJson(City.class, is);
} catch (IOException e) {
throw new RuntimeException();
}
}
});
Realm 解析 JSON 時遵循如下規則:
使用包含空值(null)的 JSON 創建對象:
對於非必須(可為空值的屬性),設置其值為 null;
對於必須(不可為空值的屬性),拋出異常;
使用包含空值(null)的 JSON 更新對象:
對於非必須(可為空值的屬性),設置其值為 null;
對於必須(不可為空值的屬性),拋出異常;
使用不包含對應屬性的 JSON: * 該屬性保持不變
7. 查詢數據
查找操作就比插入方便多了,並不需在事務中操作,直接查詢即可。
(1)findAll ——查詢
例:查詢所有的User
RealmResults<User> userList = mRealm.where(User.class).findAll();
(2)findAllAsync——非同步查詢
當數據量較大,可能會引起ANR的時候,就可以使用findAllAsync
RealmResults<User> userList = mRealm.where(User.class)
.equalTo("name", "Gavin")
.findAllAsync();
(3)findFirst ——查詢第一條數據
例:查詢User表中的第一條數據
User user2 = mRealm.where(User.class).findFirst();
(4)equalTo ——根據條件查詢
例:得到name為Gavin的用戶列表。
RealmResults<User> userList = mRealm.where(User.class)
.equalTo("name", "Gavin").findAll();
如果User中還有Dog屬性,希望根據Dog的條件來獲取用戶:
例:查詢dogs.name為二哈的User
RealmResults<User> userList = mRealm.where(User.class)
.equalTo("dogs.name", "二哈").findAll();
得到有養有dogs.name為”二哈”的用戶列表(這裡的dogs是User表中的屬性名)
(5)equalTo ——多條件查詢
當然,我們還經常要用到多條件的查詢的功能。
例:找到用戶名為“Gavin”,且dogs.name為“二哈”的User
RealmResults<User> userList = mRealm.where(User.class)
.equalTo("name", "Gavin").findAll();
RealmResults<User> userList = user5.where()
.equalTo("dogs.name", "二哈").findAll();
(6)排序
查詢結束後,還可以進行排序,就像這樣:
RealmResults<User> userList = mRealm.where(User.class) .findAll();
userList = result.sort("age"); //根據age,正序排列
userList = result.sort("age", Sort.DESCENDING);//逆序排列
(7)聚合
RealmResult自帶一些聚合方法:
RealmResults<User> results = realm.where(User.class).findAll();
long sum = results.sum("age").longValue();
long min = results.min("age").longValue();
long max = results.max("age").longValue();
double average = results.average("age");
long matches = results.size();
(8)更多查詢條件
上面就展示了equalTo的用法。當然,查詢還有更多的用法,我就不一一示例了。已知的方法如下:
sum():對指定欄位求和。
average():對指定欄位求平均值。
min(): 對指定欄位求最小值。
max() : 對指定欄位求最大值。count : 求結果集的記錄數量。
findAll(): 返回結果集所有欄位,返回值為RealmResults隊列
findAllSorted() : 排序返回結果集所有欄位,返回值為RealmResults隊列
between(), greaterThan(),lessThan(), greaterThanOrEqualTo() & lessThanOrEqualTo()
equalTo() & notEqualTo()
contains(), beginsWith() & endsWith()
isNull() & isNotNull()
isEmpty()& isNotEmpty()
8. 修改數據
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
//先查找後得到User對象
User user = mRealm.where(User.class).findFirst();
user.setAge(26);
}
});
修改也是需要在事務中操作。使用查詢語句得到數據,然後將內容改了即可。
9. 刪除數據
(1)使用deleteFromRealm()
//先查找到數據
final RealmResults<User> userList = mRealm.where(User.class).findAll();
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
userList.get(0).deleteFromRealm();
}
});
(2)使用deleteFromRealm(int index)
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
userList.deleteFromRealm(0);
}
});
(3)其他方法
userList.deleteFirstFromRealm(); //刪除user表的第一條數據
userList.deleteLastFromRealm();//刪除user表的最後一條數據
results.deleteAllFromRealm();//刪除user表的全部數據
10. 非同步任務註意點
最後在銷毀Activity或Fragment時,要取消掉非同步任務
@Override
protected void onDestroy() {
super.onDestroy();
if (deleteTask!=null&&!addTask.isCancelled()){
deleteTask.cancel();
}
}
11. 其他常見操作
API:RealmObjectSchema
(1)取消id必填
personSchema.setNullable("id", true):
(2)移除id欄位
personSchema.removeField("id");
(3)重命名
personSchema..renameField("id", "userId");
API:DynamicRealmObject
(4)獲取id
String id = obj.getString("id");
(5)為欄位設置值
obj.setString("name", "Gavin");
obj.setInt("id", 1);
obj.setLong("id", 1);
12. 資料庫版本升級
當數據結構發生變化是,需要升級資料庫。對於Realm來說,資料庫升級就是遷移操作,把原來的資料庫遷移到新結構的資料庫
例1:User類發生變化,移除age,新增個@Required的id欄位。
User版本:version 0
String name;
int age;
User版本:version 1
@Required
String id;
String name;
創建遷移類CustomMigration,需要實現RealmMigration介面。執行版本升級時的處理:
/**
* 升級資料庫
*/
class CustomMigration implements RealmMigration {
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
if (oldVersion == 0 && newVersion == 1) {
RealmObjectSchema personSchema = schema.get("User");
//新增@Required的id
personSchema
.addField("id", String.class, FieldAttribute.REQUIRED)
.transform(new RealmObjectSchema.Function() {
@Override
public void apply(DynamicReal
mObject obj) {
obj.set("id", "1");//為id設置值
}
})
.removeField("age");//移除age屬性
oldVersion++;
}
}
}
使用Builder.migration升級資料庫,將版本號改為1(原版本號:0)。當Realm發現新舊版本號不一致時,會自動使用該遷移類完成遷移操作。
RealmConfiguration config = new RealmConfiguration.Builder()
.name("myrealm.realm") //文件名
.schemaVersion(1)
.migration(new CustomMigration())//升級資料庫
.build();
例2:加入Dog類,User中加入Dog集合。
User版本:version 1
@Required
String id;
String name;
User版本:version 2
@Required
private String id;
private String name;
private RealmList<Dog> dogs;
Dog類
public class Dog extends RealmObject {
private String name;
private int age;
}
在遷移類CustomMigration中,繼續添加處理方法。
(oldVersion == 1 && newVersion == 2) {
//創建Dog表
RealmObjectSchema dogSchema = schema.create("Dog");
dogSchema.addField("name", String.class);
dogSchema.addField("age", int.class);
//User中添加dogs屬性
schema.get("User")
.addRealmListField("dogs", dogSchema)
.transform(new RealmObjectSchema.Function() {
@Override
public void apply(DynamicRealmObject obj) {
//為已存在的數據設置dogs數據
DynamicRealmObject dog = realm.createObject("Dog");
dog.set("name", "二哈");
dog.set("age", 2);
obj.getList("dogs").add(dog);
}
});
oldVersion++;
}
三、Realm官方文檔
https://realm.io/cn/docs/java/latest/
四、項目源碼下載
鏈接:https://pan.baidu.com/s/1wSoXuclZ-Nwntnw_79xkLA
密碼:oc95