房间多对多关系不起作用

Room many to many relation is not working

我遵循了 Android 中相同的 tutorial,并在我的 Room 表中创建了多对多关系。但是我得到了一个奇怪的行为,它在第一步之前有效,但对关系不起作用。

大多数情况下,我创建了一些表并尝试获取两个级别的数据。例如 Accounts - List - List .

我的表格如下:

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

频道帐户:

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 在内的所有表也都有数据。

But I am getting a strange behavior which is working till the first step, but not working for the relations.

您似乎有一个奇怪的模式,这可能是原因。也就是说,您似乎在帐户和频道之间有 2 个关系。

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

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

也就是ChannelEntity中的accountId maps/references Account.

但是,在 AccountWithChannels 中,您有一个映射 table,用于 Channel 和 Account 之间的多对多关系,按照 :-

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

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

But the Vpub list is always returned as 0.

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

I tried with foreign also but not helped.

我假设你的意思是定义外键,也许它们没有帮助,因为它们指出了问题(例如,交叉引用中的 0 table 会导致外键冲突)。

从本质上讲,您的代码似乎可以正常工作。这是基于您的代码的等效项。注意:-

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

    • @Insert returns 长
    • 理论上,如果对大型数据库使用 Int 或 int 可能会出现问题。
  • 对于 convenience/brevity 代码是 运行 在主线程上测试的。

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

  • Class 成员变量已为 class 私有,因为它们不在问题中而创建,或更改为适合。

testing/demo代码:-

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

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

根据 :-

使用了一个带注释的 class 的@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 class 根据 :-

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

Result(输出到日志):-

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 被提取如果有的话 因此我怀疑你的问题不在你问题中包含的代码中。