Realm找不到现有项目

时间:2017-06-30 20:02:02

标签: android realm realm-mobile-platform

我使用的是Realm 3.4.0并且有一个应该是单例的对象。数据库已同步。

以下是代码的简化版本:测试该对象是否存在,如果它不存在则添加它。这样做的正确方法是什么? (不应该需要copyToRealmOrUpdate,还是有其他原因导致实例变为空?)

@PrimaryKey
public long id = 1;

public static PlannerManager getInstance(Realm realm) {
    PlannerManager ourInstance = null;
    if (instanceLock == null)
        instanceLock = new ReentrantLock();
    try {
        instanceLock.lock();
        realm.refresh(); // Force getting all data from online database
        ourInstance = realm.where(PlannerManager.class).findFirst();

            if (ourInstance == null) { // The item doesn't exist
                realm.beginTransaction();
                ourInstance = realm.copyToRealm(new PlannerManager()); // Crashes sometimes with the error that an object with primary ID already exists
                realm.commitTransaction();
            }
        } finally {
          instanceLock.unlock();
        }
        return ourInstance;
    }

stacktrace的相关部分

2:9.446 Primary key value already exists: 1 .
(/Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp:189) io.realm.exceptions.RealmPrimaryKeyConstraintException: Primary key value already exists: 1 .
(/Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp:189)
    at io.realm.internal.OsObject.nativeCreateNewObjectWithLongPrimaryKey(Native Method)
    at io.realm.internal.OsObject.createWithPrimaryKey(OsObject.java:198)
    at io.realm.Realm.createObjectInternal(Realm.java:1052)
    at io.realm.PlannerManagerRealmProxy.copy(PlannerManagerRealmProxy.java:1279)
    at io.realm.PlannerManagerRealmProxy.copyOrUpdate(PlannerManagerRealmProxy.java:1268)
    at io.realm.DefaultRealmModuleMediator.copyOrUpdate(DefaultRealmModuleMediator.java:438)
    at io.realm.Realm.copyOrUpdate(Realm.java:1660)
    at io.realm.Realm.copyToRealm(Realm.java:1072)
    at com.myapp.internal.PlannerManager.getInstance(PlannerManager.java:857)

谢谢!

1 个答案:

答案 0 :(得分:1)

你的逻辑实际上有些错误。通过在事务外部执行查询,后台同步线程可以在您执行查询和开始事务之间将数据放入Realm。事务将始终将Realm移动到最新版本,因此也不需要调用refresh()。你的逻辑应该是这样的:

    realm.beginTransaction();
    ourInstance = realm.where(PlannerManager.class).findFirst();
    if (ourInstance == null) {
      ourInstance = realm.createObject(PlannerManager.class, 1);
      realm.commitTransaction();
    } else {
      realm.cancelTransaction();
    }

请注意,使用realm.copyToRealm()会导致覆盖其他设备的更改,因此对于这样的初始数据,使用createObject会更安全,因为对各个字段的更改将会正确合并。使用copyToRealm()与将所有字段实际设置为初始值相同。

例如,如果您有两个设备A和B都处于脱机状态:

  1. 启动应用并创建默认PlannerManager
  2. A修改PlannerManager
  3. 中的字段
  4. B启动应用程序,但由于A处于脱机状态,因此不知道已经创建了PlannerManager,因此它还会创建默认的PlannerManager。
  5. A和B都上线了。
  6. 由于Realm使用" last-write-wins",B现在将覆盖A所做的所有更改,因为使用copyToRealm相当于手动设置所有字段。
  7. 使用Realm.createObject()使用特殊的"默认"所有字段的指令,自动丢失到使用普通Java setter(以及copyToRealm使用的)时使用的任何显式集。