2017-06-30 7 views
0

Ich benutze Realm 3.4.0 und habe ein Objekt, das ein Singleton sein sollte. Die Datenbank wird synchronisiert.Realm findet keine vorhandenen Objekte

Hier ist eine vereinfachte Version des Codes: test ob das Objekt existiert, füge es hinzu, wenn es nicht existiert. Was ist der richtige Weg? (CopyToRealmOrUpdate sollte nicht erforderlich sein, oder gibt es einen anderen Grund, warum die Instanz null wird?)

@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; 
    } 

Relevante Teil des 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) 

Dank!

+0

Stack-Trace für Absturz wäre nett zu haben, wenn es existiert. – EpicPandaForce

+0

Ihr Primärschlüssel 'id' ist immer 1. Sie müssen für jede Instanz einen eindeutigen Primärschlüssel erstellen. – cykopath

+0

@cykopath Es sollte nur eine einzige Instanz geben, deshalb habe ich den Primärschlüssel 1, so dass er nicht verdoppelt werden kann, und suche nach einer Instanz, bevor ich die einzige Instanz einfüge, die existieren sollte. – Mackan

Antwort

1

Ihre Logik ist eigentlich etwas falsch. Wenn Sie die Abfrage außerhalb der Transaktion ausführen, kann der Hintergrundsynchronisierungs-Thread Daten in Realm zwischen der Abfrage und der Transaktion einfügen. Transaktionen verschieben das Realm immer auf die neueste Version, daher wird auch das Aufrufen von nicht benötigt. Ihre Logik sollte so etwas wie sein:

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

Hinweis, dass realm.copyToRealm() mit verursacht Änderungen von anderen Geräten außer Kraft gesetzt werden, so dass für die anfänglichen Daten wie diese, ist es sicherer, createObject zu verwenden, da Änderungen an einzelnen Feldern werden dann fusionieren korrekt. Die Verwendung von copyToRealm() entspricht dem tatsächlichen Festlegen aller Felder auf den Anfangswert.

Z. B, wenn Sie zwei Geräte A und B, die sowohl offline sind:

  1. A startet App und erstellt die Standard-PlannerManager.
  2. A ändert ein Feld in PlannerManager.
  3. B startet die App, aber da A offline ist, weiß es nicht, dass PlannerManager bereits erstellt wurde. Daher wird auch der standardmäßige PlannerManager erstellt.
  4. A und B gehen beide online.
  5. Da Realm "Last-write-wins" verwendet, überschreibt B jetzt alle von A durchgeführten Änderungen, da die Verwendung von copyToRealm dem Äquivalent der manuellen Einstellung aller Felder entspricht.

Realm.createObject() Mit Hilfe eine spezielle „default“ Anweisung für alle Felder, die verlieren automatisch auf jeden expliziten Satz wie das verwendet wird, wenn normalen Java-Setter mit (und der copyToRealm Anwendungen).

+0

Das Problem ist, dass ich die Daten von jedem Gerät, das sich an dem Konto anmeldet, zusammenführen muss, und das nur ausgeführt wird, wenn die Online-Datenbank erstmals von dem ersten Gerät erstellt wird. So gehen einzigartige Daten von den anderen Geräten verloren, bevor sie mit dem synchronisierten Bereich verbunden werden. – Mackan

+0

Aktualisierte Antwort. –

+0

Danke für diese Erklärung! Ich werde das untersuchen! Ich habe nie den Unterschied mit dem createObject verstanden, also habe ich es nie benutzt. – Mackan