Search code examples
androidandroid-room

Android Room: Modeling 1 to 0..1 relationships


I’m currently writing an app that has a class Account and a class CreditcardDetails:

public class Account implements Serializable {

@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
private long id;

@NonNull
@ColumnInfo(name = "account_type")
private AccountType type;


public class CreditCardDetails implements Serializable {

@PrimaryKey(autoGenerate = true)
private long id;

@ColumnInfo(name = "credit_card_number")
private String creditCardNumber;

@ColumnInfo(name = "credit_card_type")
private CreditCardType creditCardType;

Whenever an account is of type CREDIT_CARD, I want the account to hold an instance of credit card details. I see several approaches to model this:

  1. Have a field in the account class for the credit card details object and annotate it with @Embedded. I don’t really like this approach since most accounts are not credit card accounts which would lead to many null fields in the table.
  2. Return a multimap. I’m not sure if this works with LiveData though.
  3. Create an intermediate class. This means additional boilerplate code, which I’m not too happy with.

What exactly is the proper mechanism to deal with nested entities and Room API?


Solution

  • I did end up solving this problem by using a multimap with LiveData. This query in the AccountDAO returns a map of accounts and, if available, connected CreditCardDetails-instances:

    @Query("SELECT * FROM account LEFT JOIN credit_card_details ON account.id = credit_card_details.account_id ORDER BY list_position ASC")
    public abstract LiveData<Map<Account, CreditCardDetails>> getAccountsWithDetailsMap();
    

    In the AccountRepository, through observing the LiveData-instance, I connected accounts and CreditCardDetails and put the result into a MutableLiveData-List:

            this.accountDao.getAllSorted().observeForever(accountCreditCardDetailsMap -> {
            List<Account> accountList = new ArrayList<>();
    
            for (Account account : accountCreditCardDetailsMap.keySet()) {
    
                if (account.isCreditCardAccount()) {
                    CreditCardDetails creditCardDetails = accountCreditCardDetailsMap.get(account);
                    account.setCreditCardDetails(creditCardDetails);
                }
                accountList.add(account);
            }
    
            mutableLiveDataAccountList.postValue(accountList);
        });
    

    A class that needs the list of accounts can now observe it through this method in the repository:

        public LiveData<List<Account>> getAll() {
        return mutableLiveDataAccountList;
    }