Поведение Realm и auto increment (Android)

Я пытаюсь получить данные из Realm, используя идентификатор в качестве ссылки. Однако при запросе идентификатора я обнаружил, что Realm дает мне одинаковый идентификатор для всех элементов (ID 0). Почему автоматический идентификатор ID не используется, когда я использую аннотацию @PrimaryKey на идентификаторе модели?

Здесь укороченный класс для модели:

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

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

И запрос, который я выполняю: realm.where(Child_pages.class).equalTo("id_cp",page_id).findFirst()

+26
источник поделиться
8 ответов

В настоящий момент Realm не поддерживает автоматическое увеличение первичных ключей. Однако вы можете легко реализовать его самостоятельно, используя что-то вроде:

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

Я надеюсь, что вы можете начать.

+65
источник

Связывание Java еще не поддерживает первичные ключи, но в дорожной карте и с высоким приоритетом - см.: https://groups.google.com/forum/#!topic/realm-java/6hFqdyoH67w , В качестве обходного пути вы можете использовать этот кусок кода для генерации ключей:

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

Я использую singleton factory для генерации первичных ключей как более общее решение с лучшей производительностью (нет необходимости запрашивать max("id") каждый благодаря AtomicInteger).

В Realm Git Hub есть длинное обсуждение, если вам нужно больше контекста: Документ о том, как установить идентификатор автоматического увеличения? p >

+10
источник

Как уже упоминалось, автоинкремент еще не поддерживается.

Но для тех, кто использует kotlin и хочет иметь поведение с автоматическим приращением с областью, это одна из возможностей:

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
        }
}}
+3
источник

если ваш Id длинный, тогда:

val nextId = bgRealm.where(Branch::class.java).max("localId") as Long + 1
0
источник

К сожалению, я не могу комментировать другие ответы, но я бы хотел добавить к ответ Виниция.

Во-первых, вы не должны открывать новый экземпляр realm при поиске первичного ключа, особенно если вы его не закрываете, так как это добавляет максимально возможный счет дескриптора файла.

Во-вторых, хотя это предпочтение, вы не должны иметь примитивные типы (или эквиваленты объектов) как nullables (в Kotlin), так как это добавляет избыточное требование для проверки допустимости.

В-третьих, поскольку вы используете kotlin, вы можете просто определить метод расширения для класса Realm, например:

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

Поскольку все экземпляры RealmObject - это RealmModel, и даже объекты, которые не отслеживаются Realm, все еще являются экземплярами RealmModel, поэтому они будут доступны там, где вы используете свои классы, связанные с вашим классом. Эквивалент Java:

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

Замечание позднего редактирования: вам лучше не использовать этот подход, если вы имеете дело с данными, которые могут поступать из внешних источников, например с другими базами данных или в Интернете, потому что это приведет к конфликтам между данными в вашей внутренней базе данных, Вместо этого вы должны использовать max([id field name]). См. Предыдущие фрагменты, я уже изменил их для размещения для этого редактирования.

0
источник

Вот общее решение, лично я создаю класс с именем RealmUtils и добавляю методы этого типа:

 public static int getPrimaryKey(Class c)
{
    Realm realm = Realm.getDefaultInstance();

    String primaryKeyFied = realm.getSchema().get(c.getSimpleName()).getPrimaryKey();
    if (realm.where(c).max(primaryKeyFied)== null)
        return 1;
    int value = realm.where(c).max(primaryKeyFied).intValue();
    return value+1;
}

Почему я возвращаю 0, когда таблица пуста? Потому что я ненавижу Id со значением 0, поэтому просто измените его. Надеюсь, это поможет кому-то.

0
источник
//generates primary key
    public static int getNextKey(RealmQuery realmQuery, String fieldName) {
        try {
            Number number = realmQuery.max(fieldName);
            if (number != null) {
                return number.intValue() + 1;
            } else {
                return 1;
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            return 1;
        }
    }

пример

int id = RealmDbHelper.getNextKey(realm.where(RealmDocument.class), RealmDocument.FIELD_DOCUMENT_ID)
    realmObject.setId(id);
    realm.insert(realmObject);
0
источник

//EDIT

Я нашел это новое решение по этой проблеме

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

//Исходный ответ

Я просто хотел поделиться своей попыткой решить эту проблему, потому что я не хочу передавать основное значение Key все время. Сначала я создал класс базы данных для обработки хранилища 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
        }
    });
}

После создания класса базы данных я создал интерфейс для различения объектов с основным ключом и без него.

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

Теперь вам нужно будет отредактировать этот код предыдущего метода выполнения

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

В этом решении логика базы данных будет по-прежнему находиться в одном классе, и этот класс может передаваться каждому классу, который необходимо записать в базу данных.

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

Для дальнейших предложений не стесняйтесь оставить комментарий ниже.

-1
источник

Посмотрите другие вопросы по меткам или Задайте вопрос