从ActiveAndroid到Realm的爬坑之路(二)

Realm是作为一个Gradle插件集成到项目中的,怎么配置 文档 里说的也很清楚了,需要注意的是在官方的GitHub上是这么添加依赖的:
dependencies {
classpath "io.realm:realm-gradle-plugin:<version>-SNAPSHOT"
}

但copy的时候中间的version 应该替换成最新的版本号。然后,就可以开始使(pa)用(keng) 了。

一、 数据库实体可以不继承RealmObject,实现RealmModel接口也可,但不能继承其它父类。

文档很明确的指出,不一定要继承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

明明实现了RealmModel接口了好吗?

不可理解,去掉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;
    }

对于一个已经成型的项目,在添加任何第三方框架前都应该慎之又慎,虽然新技术听起来总是很诱人,但能不能和现有的代码相结合是需要考虑的一方面,另一方面,还有可能遇到各种大大小小的坑。这也许是这次更换数据库框架带来的最大感受。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值