Realm Android 无法在没有主键的情况下复制或更新对象

Posted

技术标签:

【中文标题】Realm Android 无法在没有主键的情况下复制或更新对象【英文标题】:Realm Android can't copyOrUpdate object without primary key 【发布时间】:2017-02-08 17:56:58 【问题描述】:

我正在使用以下架构,Realm 定义了一个拥有收藏歌曲列表的用户:

public class User extends RealmObject  
    @PrimaryKey
    Long id;
    RealmList<FavoriteSong> favoriteSongs;  


public class FavoriteSong extends RealmObject  
    String label;
    Song song;


public class Song extends RealmObject 
    @PrimaryKey
    Long id;
    String title;   

我的应用模型基本上反映了服务器模型。所以当我收到一首新的 FavoriteSong 时,我想将它保存到领域:

FavoriteSong favoriteSong = ...favorite song from server
mRealm.executeTransaction(new Realm.Transaction() 
        @Override
        public void execute(Realm realm) 
            // Add a new FavoriteSong
            realm.copyToRealm(favoriteSong); // Fails with Value already exists: 0
        

这会失败,因为FavoriteSong 中包含的歌曲已经存在于领域中,违反了主键约束。

一种解决方法是将FavoriteSong标签设置为主键:

public class FavoriteSong extends RealmObject  
    @Primary
    String label;
    Song song;

然后使用copyToRealmOrUpdate()代替copyToRealm(),它在插入之前检查所有关系的主键:

realm.copyToRealmOrUpdate(favoriteSong);

这可行,但不幸的是我不能这样做,因为可以有多个具有相同标签的收藏歌曲,而主键不允许这样做。

知道如何在没有主键且不违反主键约束的情况下将 FavoriteSong 添加到领域吗?

【问题讨论】:

您来自服务器的FavoriteSong 是否包含song; @EpicPandaForce - 是的 这是您问题的根源:FavoriteSong 没有主键,但copyToRealm() 会尝试将对象与喜欢的歌曲一起保存在其中 那你为什么不给FavoriteSong添加唯一的ID呢?例如,使用UUID.randomUUID()。这样你就不会得到重复。 FavoriteSongs 对于这个特定用户是唯一的吗?我不太了解您的模型之间的关系。 @Darwind - 是的,最喜欢的歌曲对这个用户来说是独一无二的,FavoriteSong 的服务器模型有一个“userId”字段,但我觉得没有必要在我的模型中包含它,因为有只有一个用户。为了详细说明这种关系,我们使用 FavoriteSong 的“label”字段作为类别指示符,因此我可以拥有多个带有“Classical”或“Hip-Hop”标签的喜爱歌曲。可能不是最好的组织方式,但这是后端开发人员决定构建的方式。 【参考方案1】:

请尝试

FavoriteSong favoriteSong = ...favorite song from server
mRealm.executeTransaction(new Realm.Transaction() 
        @Override
        public void execute(Realm realm) 
            Song tempSong = favoriteSong.getSong();
            favoriteSong.setSong(null);
            FavoriteSong savedFavoriteSong = realm.copyToRealm(favoriteSong);
            Song dbSong = realm.where(Song.class).equalTo(SongFields.ID, tempSong.getId()).findFirst();
            if(dbSong == null) 
                dbSong = realm.copyToRealmOrUpdate(tempSong);
            
            savedFavoriteSong.setSong(dbSong);
        

SongFields 假设您使用的是https://github.com/cmelchior/realmfieldnameshelper

【讨论】:

另一个被EpicPanda拯救的灵魂^^. 好的,所以我很愚蠢,结果我正在寻找的是试图用标签和歌曲定义一个复合主键。对困惑感到抱歉。这似乎是 Realm 的热门话题,我有几个问题可能对其他用户有帮助。如果我发布另一个关于此的具体问题,您能帮帮我吗? 复合键意味着您应该将两个属性一起附加到一个字符串字段中。 您只需要确保在Realm中创建RealmObject时获得复合ID,因为一旦创建了对象就无法修改PK。 然后对你的 Realm 访问做一个抽象【参考方案2】:

使用

import io.realm.annotations.PrimaryKey; 

而不是

import androidx.room.PrimaryKey;

【讨论】:

以上是关于Realm Android 无法在没有主键的情况下复制或更新对象的主要内容,如果未能解决你的问题,请参考以下文章

维度建模:如何创建没有代理主键的表?

如何在没有主键的情况下从连接表中创建实体

如何在没有主键的情况下从 mySQL 中的两个表中获取不常见的记录

Django:在不指定主键的情况下创建固定装置?

我可以在没有自动 ID 的情况下在 Django 中创建模型吗?

没有主键的事务复制(唯一索引)