Realm 找不到现有项目

Realm doesn't find existing items

我正在使用 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;
    }

堆栈跟踪的相关部分

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)

谢谢!

你的逻辑其实有点不对。通过在事务外进行查询,后台同步线程可能会在您进行查询和开始事务之间将数据放入 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. A 启动应用程序并创建默认 PlannerManager
  2. A 修改了 PlannerManager 中的字段。
  3. B 启动了应用程序,但由于 A 处于离线状态,它不知道 PlannerManager 已经创建,所以它也创建了默认的 PlannerManager。
  4. A和B都上线了
  5. 由于 Realm 使用 "last-write-wins" 的方式,B 现在将覆盖 A 所做的所有更改,因为使用 copyToRealm 等同于手动设置所有字段。

使用 Realm.createObject() 对所有字段使用特殊的 "default" 指令,自动丢失任何显式集,就像使用普通 Java setter 时使用的那样(以及 copyToRealm 使用)。