2015-05-04 12 views
24

Próbuję pobrać dane z Realm za pomocą ID jako odniesienia. Jednak podczas sprawdzania identyfikatora odkryłem, że Realm daje mi ten sam identyfikator dla wszystkich elementów (ID 0). Dlaczego identyfikator nie zwiększa się automatycznie, gdy używam adnotacji @PrimaryKey na identyfikatorze modelu?Zachowanie Realm and auto increment (Android)

Oto skrócona klasa dla modelu:

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

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

a zapytanie mam wykonywania jest: realm.where(Child_pages.class).equalTo("id_cp",page_id).findFirst()

+0

Czy możesz zwiększyć czytelność swojego posta? Co chcesz osiągnąć i co jest nie tak? – Mast

+0

Jest oczywiste, że otrzymujemy dane za pomocą zapytania specjalnie ID elementu, więc moje pytanie brzmi: w jaki sposób mogę uzyskać moje obiekty z bazy danych za pomocą ID i dziedzina daje wszystkie ID, ta sama wartość = 0, dlaczego nie jest automatyczne przyrost! –

Odpowiedz

63

Realm obecnie nie obsługuje automatycznego wzrastających kluczy głównych. Możesz go jednak łatwo wdrożyć, używając np.:

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

Mam nadzieję, że możesz zacząć.

+4

Ponieważ metoda 'maximumInt' została uznana za przestarzałą, musimy teraz użyć' .max ("id"). IntValue() 'zamiast tego. – atabek

+9

musimy dodać try i catch dla ArrayIndexOutOfBoundsException, gdy obiekt nie jest tworzony w bazie danych. spróbuj { return realm.where (obiekt) .max ("id"). IntValue() + 1; } catch (ArrayIndexOutOfBoundsException e) { return 0; } – beni

+4

Jeśli w bazie danych nie ma żadnych obiektów tego typu, 'max (" id ")' zwraca 'null'. Musisz to sprawdzić przed wywołaniem 'intValue()' i zwrócić pierwszy identyfikator, jeśli ma wartość 'null'. –

10

Powiązanie Java nie obsługuje jeszcze kluczy podstawowych, ale znajduje się na mapie drogowej i ma wysoki priorytet - patrz: https://groups.google.com/forum/#!topic/realm-java/6hFqdyoH67w . Jako obejście można użyć tego fragmentu kodu do generowania kluczy:

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

używam singleton factory for generating primary keys jako bardziej ogólne rozwiązania z lepszej wydajności (nie ma potrzeby, aby zapytać o max("id") za każdym razem dzięki AtomicInteger).

Istnieje długa dyskusja w Realm Git Hub, jeśli potrzebujesz więcej kontekstu: Document how to set an auto increment id?

+0

Powyższy kod działa dobrze dla mnie, ale mam NPE po raz pierwszy zamiast AIOOBE. BTW Użyłem 'long' jako klucza podstawowego" id ". – dakshbhatt21

+0

ten fragment jest dość stary - jakiej wersji sfery używasz? – zacheusz

+0

Używam wersji 2.2.0 – dakshbhatt21

-1

// EDIT

znalazłem to nowe rozwiązanie na ten problem

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

// Oryginalna odpowiedź

Po prostu chcę d, aby udostępnić moją próbę rozwiązania tego problemu, ponieważ nie chcę przez cały czas przekazywać podstawowej wartości klucza. Najpierw utworzyłem Database-Class, aby obsłużyć przechowywanie 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 
     } 
    }); 
} 

Po utworzeniu bazy danych klasy I stworzył interfejs rozróżniać przedmioty z kluczem podstawowym i bez.

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

Teraz wystarczy zmienić ten kod poprzedniego wykonać metoda

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

Dzięki takiemu rozwiązaniu logika baza danych nadal będzie w jednej klasie, a ta klasa może przekazywana do każdej klasy, które musi pisać do bazy danych.

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

W celu uzyskania dalszych sugestii, zostaw komentarz poniżej.

0

jeśli Id jest długa następnie:

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

Jak już wspomniano, auto-przyrost nie jest jeszcze obsługiwany.

Ale dla tych, którzy używają Kotlin i chce mieć zachowanie automatycznego przyrostu z królestwa, jest to jedna z możliwości:

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

Niestety, nie mogę wypowiedzieć się na temat innych odpowiedzi, ale Chciałbym dodać do Vinicius` answer.

Po pierwsze, nie powinieneś otwierać nowej instancji dziedziny, szukając klucza podstawowego, zwłaszcza gdy ich nie zamykasz, ponieważ zwiększa to maksymalną możliwą liczbę deskryptorów plików.

Po drugie, chociaż jest to korzystne, nie należy mieć prymitywne typy (lub odpowiedniki Object) jak nullables (w Kotlin), a to dodaje redundantny wymóg, aby sprawdzić opcje dopuszczania wartości null.

Po trzecie, skoro używasz Kotlin, można po prostu określić metodę rozszerzenia do Realm klasy, takie jak:

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

Ponieważ wszystkie RealmObject przypadki są RealmModel, a nawet przedmioty, które nie są śledzone przez Realm wciąż jest instancjami RealmModel, dlatego będzie dostępny tam, gdzie kiedykolwiek używasz klas związanych z rzeczywistością. odpowiednik Java byłoby:

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

Późno edit note: Lepiej nie za pomocą tej metody, jeśli masz do czynienia z danymi, które mogą pochodzić ze źródeł zewnętrznych, takich jak innych baz danych lub w internecie, bo to powodują kolizje między danymi w wewnętrznej bazie danych. Zamiast tego powinieneś użyć max([id field name]). Zobacz poprzednie fragmenty, już je zmodyfikowałem, aby uwzględnić tę zmianę.