【问题标题】:Room many to many relation is not working房间多对多关系不起作用
【发布时间】:2021-08-25 02:39:45
【问题描述】:

我遵循了来自 Android 的相同 tutorial 并在我的 Room 表中创建了多对多关系。但是我遇到了一个奇怪的行为,它一直有效到第一步,但不适用于关系。

主要是我创建了一些表并尝试获取数据直到两个级别。例如帐户 - 列表 - 列表。

我的表格如下:

@Dao
public abstract class AccountDAO {

    /*Get accounts*/
    @Transaction
    @Query("SELECT * FROM AccountModel")
    public abstract LiveData<DataWithAccounts> getAccountList();

    /*Get the account with respected channels*/
    @Transaction
    @Query("SELECT * FROM AccountEntity")
    public abstract LiveData<AccountWithChannels> getAccountWithChannels();


    /*Get the account with respected channels and vpubs*/
    @Transaction
    @Query("SELECT * FROM AccountEntity")
    public abstract LiveData<AccountWithChannelsAndVpubs> getAccountWithChannelsAndVpubs();

    @Transaction
    @Query("SELECT * FROM AccountEntity WHERE accId = :id")
    public abstract AccountWithChannelsAndVpubs loadData(long id);

    /*
     *Insert the object in database
     * @param account list, object to be inserted
     */
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public abstract void insertAccountModel(AccountModel accountModel);

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public abstract void insertAccountList(List<AccountEntity> data);

    /*
     * update the object in database
     * @param account, object to be updated
     */
    @Update
    public abstract void updateAccountList(AccountModel repos);

    /*
     * delete the object from database
     * @param account, object to be deleted
     */
    @Delete
    public abstract void deleteAccountList(AccountModel note);
}

AccountWithChannels:

public class AccountWithChannels {
    @Embedded public AccountEntity accounts;

    @Relation(
            parentColumn = "accId",
            entityColumn = "channelId",
            associateBy = @Junction(AccountChannelCrossRef.class)
    )
    public List<ChannelEntity> channels;

    public AccountEntity getAccounts() {
        return accounts;
    }

    public void setAccounts(AccountEntity accounts) {
        this.accounts = accounts;
    }

    public List<ChannelEntity> getChannels() {
        return channels;
    }

    public void setChannels(List<ChannelEntity> channels) {
        this.channels = channels;
    }
}

ChannelWithVpubs:

public class ChannelWithVpubs {
    @Embedded
    public ChannelEntity channel;

    @Relation(
            parentColumn = "channelId",
            entityColumn = "vPubId",
            associateBy = @Junction(ChannelVpubCrossRef.class)
    )
    public List<VPubEntity> vPubs;

    public ChannelEntity getChannel() {
        return channel;
    }

    public void setChannel(ChannelEntity channel) {
        this.channel = channel;
    }

    public List<VPubEntity> getvPubs() {
        return vPubs;
    }

    public void setvPubs(List<VPubEntity> vPubs) {
        this.vPubs = vPubs;
    }
}

AccountWithChannelsAndVpubs:

public class AccountWithChannelsAndVpubs {

    @Embedded
    public AccountEntity account;
    @Relation(
            entity = ChannelEntity.class,
            parentColumn = "accId",
            entityColumn = "accountId"
    )
    public List<ChannelWithVpubs> channelWithVpubs;

    public AccountEntity getAccount() {
        return account;
    }

    public void setAccount(AccountEntity account) {
        this.account = account;
    }

    public List<ChannelWithVpubs> getChannelWithVpubs() {
        return channelWithVpubs;
    }

    public void setChannelWithVpubs(List<ChannelWithVpubs> channelWithVpubs) {
        this.channelWithVpubs = channelWithVpubs;
    }
}

ChannelVpubCrossRef:

@Entity(primaryKeys = {"channelId", "vPubId"})
public class ChannelVpubCrossRef {
    public int channelId;
    public int vPubId;
}

在使用交叉引用创建关系后,我调用了以下方法来获取包含帐户、列表和列表的所有数据。

private List<ChannelWithvPub> getAllVpubs(AccountEntity mAccountEntity){
    List<ChannelWithvPub> mData = new ArrayList<>();

    AccountWithChannelsAndVpubs accWithChnlNvpubs = database.accountDao().loadData(mAccountEntity.getAccId());

    List<ChannelWithVpubs> mdata = accWithChnlNvpubs.getChannelWithVpubs();

    for(int i=0; i<mdata.size(); i++){

        ChannelWithVpubs mChannelWithVpubs = mdata.get(i);

        for(int j=0; j<mChannelWithVpubs.getvPubs().size(); j++){

            VPubEntity mVpub = mChannelWithVpubs.getvPubs().get(j);
            ChannelWithvPub newData = new ChannelWithvPub(mAccountEntity.getAccId(), 
   mChannelWithVpubs.getChannel().getChannelId(), mVpub);
            mData.add(newData);
        }
    }

   return mData;
}

但奇怪的是我得到了帐户和频道列表。但是 Vpub 列表总是返回为 0。

我也尝试过外国人,但没有帮助。我确定我在这里做错了,我无法检测到。但数据正确插入,包括 Vpub 在内的所有表也有数据。

【问题讨论】:

    标签: android android-room android-room-relation


    【解决方案1】:

    但我遇到了一个奇怪的行为,它一直有效到第一步,但不适用于关系。

    您似乎有一个奇怪的架构,这可能是原因。也就是说,您似乎在 Account's 和 Channel's 之间有 2 个关系。

    根据AccountWithChannelsAndVpubs,在频道实体上,您似乎有一个频道作为单个帐户的子帐户,其中包括:-

    @Relation(
                entity = ChannelEntity.class,
                parentColumn = "accId",
                entityColumn = "accountId"
        )
    

    即 ChannelEntity 中的 accountId 映射/引用 Account。

    但是,在 AccountWithChannels 中,您有一个映射表,用于通道和帐户之间的多对多关系,如下所示:-

    @Relation(
                parentColumn = "accId",
                entityColumn = "channelId",
                associateBy = @Junction(AccountChannelCrossRef.class)
        )
    

    虽然这可以发挥作用,但它可能会导致很大的混乱,也许这种混乱就是结果。

    但是 Vpub 列表总是返回为 0。

    当您使用原语 (int) 作为 id 时,这些默认为 0,因此您可能按照默认值插入,因此为 0。

    我也尝试过外国人,但没有帮助。

    我假设您的意思是定义 ForeignKey,也许它们没有帮助,因为它们表明了问题(例如,交叉引用表中的 0 会导致 ForeignKey 冲突)。

    本质上,您的代码似乎可以工作。这是基于您的代码的等价物。注意:-

    • 我总是使用 Long 作为 id(在生成的地方):-

      • @Insert 返回一个 Long
      • 理论上,如果对大型数据库使用 Int 或 int 可能会导致问题。
    • 为了方便/简洁,代码在主线程上运行测试。

    • 为简洁起见,省略了 getter 和 setter。

    • 已将类成员变量设置为私有类,因为它们不在问题中,或已更改为适合。

    测试/演示代码:-

    AccountEntity

    @Entity
    class AccountEntity {
        @PrimaryKey
        private Long accId;
        private String accName;
    
        public AccountEntity() {}
    
        @Ignore
        public AccountEntity(String accountName) {
            this.accName = accountName;
        }
    }
    

    ChannelEntity

    @Entity
    class ChannelEntity {
        @PrimaryKey
        private Long channelId;
        private String channelName;
        private long accountId;
    
        public ChannelEntity(){}
    
        @Ignore
        public ChannelEntity(String channelName, long accountId) {
    
            this.channelName = channelName;
            this.accountId = accountId;
        }
    }
    

    VPubEntity

    @Entity
    class VPubEntity {
        @PrimaryKey
        private Long vPubId;
        private String vPubName;
    
        public VPubEntity(){}
    
        @Ignore
        public VPubEntity(String vPubName) {
            this.vPubName = vPubName;
        }
    }
    

    AccountChannelCrossref

    @Entity(
            primaryKeys = {"accId","channelId"}
            )
    class AccountChannelCrossRef {
        private long accId;
        private long channelId;
    
        public AccountChannelCrossRef(){}
    
        @Ignore
        public AccountChannelCrossRef(long accId, long channelId) {
            this.accId = accId;
            this.channelId = channelId;
        }
    }
    

    ChannelVpubCrossRef(从 int 更改为 long)

    @Entity(
            primaryKeys = {"channelId", "vPubId"}
            )
    public class ChannelVpubCrossRef {
        public long channelId;
        public long vPubId;
    }
    

    ChannelWithVpubsAccountWithChannelsAccountWithChannelsAndVpubs 类按原样复制,因此未包含在内。

    使用了单个 @Dao 注释类:-

    @Dao
    abstract class AllDao {
    
        @Insert
        abstract long insert(AccountEntity accountEntity);
        @Insert
        abstract long insert(ChannelEntity channelEntity);
        @Insert
        abstract long insert(VPubEntity vPubEntity);
        @Insert
        abstract long insert(AccountChannelCrossRef accountChannelCrossRef);
        @Insert
        abstract long insert(ChannelVpubCrossRef channelVpubCrossRef);
    
        @Query("INSERT INTO accountchannelcrossref VALUES(:accId,:channelId)")
        abstract long insertAccountChannelCrossRef(long accId, long channelId);
        @Query("INSERT INTO channelvpubcrossref VALUES(:channelId,:vPubId)")
        abstract long insertChannelVPubCrossRef(long channelId,long vPubId);
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        public abstract void insertAccountList(List<AccountEntity> data);
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        public abstract void insertChannelList(List<ChannelEntity> data);
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        public abstract void insertVPubList(List<VPubEntity> data);
    
        @Transaction
        @Query("SELECT * FROM AccountEntity")
        //public abstract LiveData<AccountWithChannelsAndVpubs> getAccountWithChannelsAndVpubs();
        public abstract List<AccountWithChannelsAndVpubs> getAccountWithChannelsAndVpubs();
    }
    

    @Database 类是 TheDatabase 根据:-

    @Database(
            entities = {
                    AccountEntity.class,
                    ChannelEntity.class,
                    VPubEntity.class,
                    AccountChannelCrossRef.class,ChannelVpubCrossRef.class
            },
            version = 1
    )
    abstract class TheDatabase extends RoomDatabase {
    
        abstract AllDao getAllDao();
    
        private static volatile TheDatabase instance = null;
        public static TheDatabase getInstance(Context context) {
            if (instance == null) {
                instance = Room.databaseBuilder(
                        context,
                        TheDatabase.class,
                        "my.db"
                )
                        .allowMainThreadQueries()
                        .build();
            }
            return instance;
        }
    }
    

    最后运行上面的代码是使用以下代码完成的:-

    public class MainActivity extends AppCompatActivity {
    
        TheDatabase db;
        AllDao dao;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            db = TheDatabase.getInstance(this);
            dao = db.getAllDao();
    
            long a1 = dao.insert(new AccountEntity("Account1"));
            long a2 = dao.insert(new AccountEntity("Account2"));
            long a3 = dao.insert(new AccountEntity("Account3"));
    
            long c1 = dao.insert(new ChannelEntity("Channel1",a1)); //???? N2M 1 channel Many accounts
            long c2 = dao.insert(new ChannelEntity("Channel2",a2));
            long c3 = dao.insert(new ChannelEntity("Channel3",a3));
    
            long v1 = dao.insert(new VPubEntity("VPub1"));
            long v2 = dao.insert(new VPubEntity("VPub2"));
            long v3 = dao.insert(new VPubEntity("VPub3"));
    
            // ???? M2M between account and channel
            dao.insert(new AccountChannelCrossRef(a1,c1));
            dao.insert(new AccountChannelCrossRef(a1,c3));
            dao.insert(new AccountChannelCrossRef(a2,c2));
            dao.insert(new AccountChannelCrossRef(a3,c1));
            dao.insert(new AccountChannelCrossRef(a3,c2));
            dao.insert(new AccountChannelCrossRef(a3,c3));
    
            ChannelVpubCrossRef cvxref1 = new ChannelVpubCrossRef();
            cvxref1.channelId = c1;
            cvxref1.vPubId = v1;
            dao.insert(cvxref1);
            cvxref1.vPubId = v2;
            dao.insert(cvxref1);
            cvxref1.vPubId = v3;
            dao.insert(cvxref1);
            cvxref1.channelId = c2;
            cvxref1.vPubId = v3;
            dao.insert(cvxref1);
    
            String TAG = "AWCAVINFO";
            for(AccountWithChannelsAndVpubs awcav: dao.getAccountWithChannelsAndVpubs()) {
                Log.d(TAG,"Account is " + awcav.account.getAccName());
                for (ChannelWithVpubs cwv: awcav.channelWithVpubs) {
                    Log.d(TAG,"\tChannel is " + cwv.channel.getChannelName());
                    for(VPubEntity v: cwv.vPubs) {
                        Log.d(TAG,"\t\tVPub is " + v.getVPubName());
                    }
                }
            }
        }
    }
    

    结果(输出到日志):-

    D/AWCAVINFO: Account is Account1
    D/AWCAVINFO:    Channel is Channel1
    D/AWCAVINFO:        VPub is VPub1
    D/AWCAVINFO:        VPub is VPub2
    D/AWCAVINFO:        VPub is VPub3
    D/AWCAVINFO: Account is Account2
    D/AWCAVINFO:    Channel is Channel2
    D/AWCAVINFO:        VPub is VPub3
    D/AWCAVINFO: Account is Account3
    D/AWCAVINFO:    Channel is Channel3
    

    因此提取了 VPub如果有的话,因此我怀疑您的问题不在您问题中包含的代码中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-17
      • 1970-01-01
      • 1970-01-01
      • 2021-12-08
      • 1970-01-01
      相关资源
      最近更新 更多