领域和自增行为(Android)

27

我正在尝试使用ID作为参考从Realm获取数据。但是,当查询ID时,我发现Realm为所有元素(ID为0)提供相同的ID。为什么在模型的ID上使用@PrimaryKey注释时ID不会自动递增?

这是该模型的缩短类:

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()


请问您能否提高您的帖子可读性?您想要实现什么,出了什么问题? - Mast
很明显我们使用查询特定元素的ID来获取数据,所以我的问题是如何通过ID从数据库中获取对象,而realm给出了所有ID,相同的值=0,为什么不是自动增量! - Bachlet Tansime
8个回答

66

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

我希望这能够帮助您入门。


4
由于maximumInt方法已经被弃用,现在我们需要使用.max("id").intValue()来替代。 - atabek
9
当数据库中没有创建对象时,我们需要为ArrayIndexOutOfBoundsException添加try和catch语句。尝试{ 返回realm.where(object).max(“id”)。intValue()+ 1; } catch(ArrayIndexOutOfBoundsException e){ 返回0; } - beni
4
如果数据库中没有那种类型的对象,则max("id")会返回null。因此,在调用intValue()之前,您必须检查它是否为null,如果是,则返回第一个id。 - David Miguel
有人能为我解释一下这个的原因吗?为什么这件事情没有默认包含在内呢? - Neon Warge
1
@NeonWarge,因为自增主键在分布式环境下无法正常工作,这是 Realm Sync 的原因。 - EpicPandaForce

10

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

我使用单例工厂来生成主键,这是一个更通用且性能更好的解决方案(由于使用AtomicInteger,不需要每次查询max("id"))。

如果需要更多上下文,请参考 Realm Git Hub 上的长篇讨论:如何设置自动递增 id?


以上代码对我来说运行良好,但是我第一次得到了NPE而不是AIOOBE。顺便说一下,我使用“long”作为主键“id”。 - dakshbhatt21
这段代码有点老了 - 你用的是哪个版本的 Realm? - zacheusz
我正在使用2.2.0版本。 - dakshbhatt21
1
我不知道如果高优先级问题在等待列表中等了4年,他们什么时候会处理到中等优先级的问题。 - Farid

4

正如已经提到的那样,目前还不支持自增。

但是对于那些使用 kotlin 并希望在 realm 中具有自增行为的人来说,这是其中一种可能性:

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

1

这里提供一种通用解决方案,个人创建了一个名为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
如果你的Id是Long类型的话:
val nextId = bgRealm.where(Branch::class.java).max("localId") as Long + 1

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

很遗憾,我不能对其他答案发表评论,但我想补充Vinicius的回答

首先,在寻找主键时,你不应该打开一个新的领域实例,尤其是当你不关闭它们时,因为这会增加最大可能的文件描述符计数。

其次,虽然这是一种偏好,但你不应该将原始类型(或对象等效物)作为可为空的类型(在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实例,因此它将在您使用与Realm相关的类的任何地方都可用。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])。请参见前面的代码片段,我已经修改它们以适应此编辑。

-1

// 编辑

我在这个问题上找到了一个新的解决方案。

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

// 翻译后的文本

我想分享一下解决这个问题的尝试,因为我不想一直传递主键值。首先,我创建了一个数据库类来处理存储 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;
    }
}

如需更多建议,请随时在下方留言。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接