Realm是作为一个Gradle插件集成到项目中的,怎么配置 文档 里说的也很清楚了,需要注意的是在官方的GitHub上是这么添加依赖的: dependencies { classpath "io.realm:realm-gradle-plugin:<version>-SNAPSHOT" } 但copy的时候中间的version 应该替换成最新的版本号。然后,就可以开始使(pa)用(keng) 了。
文档很明确的指出,不一定要继承RealmObject,实现RealmModel接口加@RealmClass注解一样可以工作。 不过你要是继承了别的类,就像这样:
@RealmClass public class DepartureRealm extends Model implements RealmModel{ @PrimaryKey public long key; ... }那问题就来了: Error:(12, 8) 错误: Realm model classes must either extend RealmObject or implement RealmModel to be considered a valid model class
不可理解,去掉extends又能正常运行了。这样就直接否定了我想直接使用ActiveAndroid的实体的想法,最关键的是,既然不能继承其他类,提供这么个功能的意义何在啊?
传统数据库很常见的概念在这里是找不到的,主键不自增虽然有点不方便,但使用UUID也不失为一个好办法。只是只有主键相同,它才帮你更新这一点,就让人满脑子问号了。而且它不支持左连右连,它尽量的将连接查询给透明化,你只需要建立好实体之间的关系,就能通过字段名直接查到关联表的数据。不得不说,这样设计很酷炫也很方便,然而,对于一个已存在的项目,就必须去修改它的表结构了,这必然会影响到整个项目。 为了解决这些问题,通过只修改数据库Service层的相关类达到使用Realm而又能随时切换回来的目的,就必须提供一个转换机制,如下图:
而且因为Realm只支持主键唯一的特点,每次Save时,还得判断字段是不是有唯一性约束,有就查询数据库是否存在此字段值,存在就更新,不存在才生成新的ID并插入。总的changeActiveToRealm方法如下:
//mID, 转换到RealmObject后,为其生成UUID,并将其通过这个参数传递出去。 public static RealmModel changeActiveToRealm(Model object, Bundle mID) { Model temp; if (object == null) { return null; } else { temp = object; } Field[] fields = temp.getClass().getFields(); String className = temp.getClass().getName(); ... try { //加载Active实体对应的Realm实体。 Class obtainClass = Class.forName(className.replace("Active", "Realm").replace("active", "realmobj")); RealmModel obtainObj = (RealmObject) obtainClass.newInstance(); Field tempObtainField; String fieldName; boolean findUnique = false; Realm realm = Realm.getDefaultInstance(); //记录下所有需要提供唯一性约束的字段。(根据Column注解) List<Field> group = new ArrayList<>(); for (Field field : fields) { Column c = field.getAnnotation(Column.class); if (c != null) { Column.ConflictAction[] a = c.onUniqueConflicts(); if (a.length != 0 && a[0] == Column.ConflictAction.REPLACE) { group.add(field); } } } if(group.size() != 0) { RealmQuery query = realm.where(obtainClass); for (Field f : group) { Object uniqueValue = f.get(temp); if (uniqueValue instanceof Integer) { query.equalTo(f.getName(), (Integer) uniqueValue); } else { query.equalTo(f.getName(), String.valueOf(uniqueValue)); } } RealmModel clazz = query.findFirst(); if (clazz != null) { Log.i("feng", "找到唯一性约束冲突 class Name :" + clazz.getClass().getSimpleName() +" 更新数据库数据"); obtainObj = realm.copyFromRealm(clazz); findUnique = true; } realm.close(); } //将字段值一一映射 for (Field field : fields) { fieldName = field.getName(); tempObtainField = obtainObj.getClass().getField(fieldName); tempObtainField.setAccessible(true); tempObtainField.set(obtainObj, field.get(temp)); } //是否生成ID if (!findUnique) { Long tempmID = UUID.randomUUID().getLeastSignificantBits(); Field o = obtainClass.getField("key"); if (o.getName().equals("key")) { int t = tempmID.intValue(); o.set(obtainObj, (long) t); mID.putLong("id", (long) t); } } else { mID.putLong("id", (long)obtainClass.getField("key").get(obtainObj)); } return obtainObj; } catch (ClassNotFoundException e) { ... } }而changeRealmToActive 的基本思路也差不多,都是通过反射获得实体的值,并将它们映射到新的实体中去。 至此,应用并不知道底层的数据库已经更换了,它一直用的都是Active的实体对象,而只需要在Service层中增加一个flag做判断,来回切换两个数据库都不是问题了。 还有一种实现思路是不通过反射,而是用JSON字符串做中转,然而效率上肯定是不如直接反射拿到对象并且直接操作了。
上篇文章已经介绍了懒加载机制,官方自豪的宣称因为查询出来并不占用内存,所以你们放心地查出来所有的数据,想要哪段就截取哪段。(提供接口不是更好吗?)
//从查询的所有数据中取出需要的。(脱离Realm的控制) public static <E extends RealmModel> List<E> getLimitList( RealmResults<E> data, int offset, int limit) { List<E> obtainList = new ArrayList(); Realm realm = Realm.getDefaultInstance(); if (data.size() == 0 ){ return obtainList; } for (int i = offset; i < offset + limit; i++) { if (i >= data.size()) { break; } E temp = realm.copyFromRealm(data.get(i)); obtainList.add(temp); } realm.close(); return obtainList; }对于一个已经成型的项目,在添加任何第三方框架前都应该慎之又慎,虽然新技术听起来总是很诱人,但能不能和现有的代码相结合是需要考虑的一方面,另一方面,还有可能遇到各种大大小小的坑。这也许是这次更换数据库框架带来的最大感受。