2015-05-04 2 views
24

Sto cercando di ottenere dati da Realm utilizzando un ID come riferimento. Tuttavia, durante la ricerca di un ID, ho trovato che Realm mi sta dando lo stesso ID per tutti gli elementi (ID di 0). Perché l'ID non viene incrementato automaticamente quando utilizzo l'annotazione @PrimaryKey sull'ID del modello?Comportamento ambito e incremento automatico (Android)

Ecco la classe accorciato per il modello:

public class Child_pages extends RealmObject { 
    @PrimaryKey 
    private int id_cp; 

    private int id; 
    private String day; 
    private int category_id; 

E la domanda che sto eseguendo è: realm.where(Child_pages.class).equalTo("id_cp",page_id).findFirst()

+0

la prego di aumentare la leggibilità del tuo post? Cosa vuoi raggiungere e cosa non va? – Mast

+0

È chiaro che otteniamo dati utilizzando query appositamente ID di elemento, quindi la mia domanda è come posso ottenere i miei oggetti dal database usando ID e realm fornisce tutti gli ID, lo stesso valore = 0, perché non è Incremento automatico! –

risposta

63

Realm attualmente non supporta Auto incremento chiavi primarie. Tuttavia è possibile implementarlo facilmente usando qualcosa come:

public int getNextKey() { 
    try { 
     Number number = realm.where(object).max("id"); 
     if (number != null) { 
      return number.intValue() + 1; 
     } else { 
      return 0; 
     } 
    } catch (ArrayIndexOutOfBoundsException e) { 
     return 0; 
    } 
} 

Spero che possa farti iniziare.

+4

Poiché il metodo 'maximumInt' è stato deprecato, ora dobbiamo usare' .max ("id"). IntValue() '. – atabek

+9

abbiamo bisogno di aggiungere un try e catch per ArrayIndexOutOfBoundsException quando l'oggetto non viene creato nel database. try { ritorno realm.where (oggetto) .max ("id") intValue() + 1.; } catch (ArrayIndexOutOfBoundsException e) { return 0; } – beni

+4

Se non ci sono oggetti di quel tipo nel db, max ("id") 'restituisce' 'null'. Quindi è necessario verificarlo prima di chiamare 'intValue()', e restituire il primo id se è 'null'. –

10

L'associazione Java non supporta ancora le chiavi primarie, ma è sulla roadmap e con priorità elevata - vedere: https://groups.google.com/forum/#!topic/realm-java/6hFqdyoH67w . Come soluzione alternativa è possibile utilizzare questo pezzo di codice per le chiavi che generano:

int key; 
try { 
    key = realm.where(Child_pages.class).max("id").intValue() + 1; 
} catch(ArrayIndexOutOfBoundsException ex) { 
key = 0; 
} 

Io uso singleton factory for generating primary keys come una soluzione più generica con la migliore prestazione (non c'è bisogno di interrogare per max("id") ogni volta grazie a AtomicInteger).

C'è una lunga discussione nel Reame Git Hub se avete bisogno di più contesto: Document how to set an auto increment id?

+0

Il codice sopra funziona correttamente per me, ma ho ottenuto NPE per la prima volta invece di AIOOBE. BTW Ho usato il 'long' come chiave primaria" id ". – dakshbhatt21

+0

questo snippet è piuttosto vecchio - quale versione di regno usi? – zacheusz

+0

Sto usando 2.2.0 – dakshbhatt21

-1

// EDIT

ho trovato questa nuova soluzione a questo problema

@PrimaryKey 
private Integer _id = RealmAutoIncrement.getInstance().getNextIdFromDomain(AccountType.class); 

// Risposta originale

I just wante d per condividere il mio tentativo di risolvere questo problema, perché non voglio passare un valore di chiave primaria tutto il tempo. Per prima cosa ho creato una classe database per gestire l'archiviazione di un oggetto RealmObject.

public class RealmDatabase { 
Realm realm; 

public RealmDatabase() { 
    RealmConfiguration realmConfiguration = new RealmConfiguration.Builder() 
      .name("test").schemaVersion(1).build(); 

    try { 
     realm = Realm.getInstance(realmConfiguration); 
    } catch (Exception e) { 
     Log.e("Realm init failed", e.getMessage()); 
    } 
} 

protected void saveObjectIntoDatabase(final RealmObject object) { 
    realm.executeTransactionAsync(new Realm.Transaction() { 
     @Override 
     public void execute(Realm bgRealm) { 
      bgRealm.copyToRealm(object);  
     } 
    }, new Realm.Transaction.OnSuccess() { 
     @Override 
     public void onSuccess() { 
      // onSuccess 
     } 
    }, new Realm.Transaction.OnError() { 
     @Override 
     public void onError(Throwable error) { 
      // onError 
     } 
    }); 
} 

Dopo aver creato il database di classe ho creato un'interfaccia di distinguere tra oggetti con una chiave primaria e senza.

public interface AutoIncrementable { 
    public void setPrimaryKey(int primaryKey); 
    public int getNextPrimaryKey(Realm realm); 
} 

Ora sarà solo bisogno di modificare il codice del precedente metodo Execute

if(object instanceof AutoIncrementable){ 
    AutoIncrementable autoIncrementable = (AutoIncrementable) object; 
    autoIncrementable.setPrimaryKey(autoIncrementable.getNextPrimaryKey(bgRealm)); 
    bgRealm.copyToRealm((RealmObject)autoIncrementable); 
} else { 
    bgRealm.copyToRealm(object); 
} 

Con questa soluzione la logica del database sarà ancora in una classe e questa classe può tramandata ad ogni classe che ha bisogno di scrivere nel database.

public class Person extends RealmObject implements AutoIncrementable{ 

    @PrimaryKey 
    public int id; 
    public String name; 

    @Override 
    public void setPrimaryKey(int primaryKey) { 
     this.id = primaryKey; 
    } 

    @Override 
    public int getNextPrimaryKey(Realm realm) { 
     return realm.where(Person.class).max("id").intValue() + 1; 
    } 
} 

Per ulteriori suggerimenti sentiti libero di lasciare un commento qui sotto.

0

se l'ID è lungo quindi:

val nextId = bgRealm.where(Branch::class.java).max("localId") as Long + 1 
3

Come già accennato, incremento automatico non è ancora supportato.

Ma per coloro che utilizzano Kotlin e vuole avere un comportamento auto-incremento con reame, questo è una delle possibilità:

open class Route(
    @PrimaryKey open var id: Long? = null, 
    open var total: Double? = null, 
    open var durationText: String? = null, 
    open var durationMinutes: Double? = null, 
    open var distanceText: String? = null, 
    open var distanceMeters: Int? = null): RealmObject() { 

companion object { 
    @Ignore var cachedNextId:Long? = null 
     get() { 
      val nextId = if (field!=null) field?.plus(1) 
          else Realm.getDefaultInstance()?.where(Route::class.java)?.max("id")?.toLong()?.plus(1) ?: 1 
      Route.cachedNextId = nextId 
      return nextId 
     } 
}} 
0

Purtroppo, non posso commentare le altre risposte, ma Mi piacerebbe aggiungere a Vinicius` answer.

primo luogo, non si dovrebbe aprire una nuova istanza regno, quando alla ricerca di chiave primaria, soprattutto quando non li chiude, come questo aggiunge al conteggio massimo possibile descrittore di file.

secondo luogo, anche se questo è una preferenza, si dovrebbe non avere tipi primitivi (o equivalenti) come oggetto nullables (in Kotlin), come questo aggiunge requisito ridondante per controllare nullability.

In terzo luogo, dal momento che si sta utilizzando Kotlin, si può solo definire un metodo di estensione per Realm di classe, come ad esempio:

fun Realm.getNextId(model: RealmModel, idField : String) : Long 
{ 
    val count = realm.where(model::class.java).max(idField) 
    return count + 1L 
} 

Dal momento che tutte le istanze sono RealmObjectRealmModel, e anche oggetti che non sono monitorati dal Il reame sono ancora le istanze RealmModel, quindi sarà disponibile ovunque tu usi le classi relative al tuo reame. Java equivalente sarebbe:

static long getNextId(RealmModel object, Realm realm, String idField) 
{ 
    long count = realm.where(object.class).max(idField); 
    return count + 1; 
} 

tardo modifica nota: È meglio non utilizzare questo approccio se hai a che fare con i dati che potrebbero provenire da fonti esterne, come altri database o sul web, perché questo causare collisioni tra i dati nel database interno. Invece dovresti usare max([id field name]). Vedi i frammenti precedenti, li ho già modificati per accogliere questa modifica.