0

我遵循了 Android 中的相同教程,并在我的 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);
}

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

在创建具有交叉引用的关系后,我调用了以下方法来获取包含 Account、List 和 List 的所有数据。

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

4

1 回答 1

2

但是我得到了一个奇怪的行为,它一直工作到第一步,但没有为关系工作。

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

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

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

那就是 ChannelEntity 中的 accountId 映射/引用帐户。

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

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

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

但 Vpub 列表始终返回为 0。

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

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

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

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

  • 我总是将 Long 用于 id (生成的地方):-

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

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

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

测试/演示代码:-

账户实体

@Entity
class AccountEntity {
    @PrimaryKey
    private Long accId;
    private String accName;

    public AccountEntity() {}

    @Ignore
    public AccountEntity(String accountName) {
        this.accName = accountName;
    }
}

通道实体

@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 ,因此我怀疑您的问题不在您问题中包含的代码中。

于 2021-08-25T09:36:51.983 回答