Поведение 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()
Ответы
Ответ 1
В настоящий момент 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;
}
}
Я надеюсь, что вы можете начать.
Ответ 2
Связывание 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 >
Ответ 3
Как уже упоминалось, автоинкремент еще не поддерживается.
Но для тех, кто использует 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
}
}}
Ответ 4
если ваш Id длинный, тогда:
val nextId = bgRealm.where(Branch::class.java).max("localId") as Long + 1
Ответ 5
К сожалению, я не могу комментировать другие ответы, но я бы хотел добавить к ответ Виниция.
Во-первых, вы не должны открывать новый экземпляр 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])
. См. Предыдущие фрагменты, я уже изменил их для размещения для этого редактирования.
Ответ 6
Вот общее решение, лично я создаю класс с именем 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, поэтому просто измените его. Надеюсь, это поможет кому-то.
Ответ 7
//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);
Ответ 8
//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;
}
}
Для дальнейших предложений не стесняйтесь оставить комментарий ниже.