Skip to content Skip to sidebar Skip to footer

Room Db With Many To Many Relations : Returns Single Entry On Querying For Filtered Data

I have 3 enitities Person, PersonTagRelation and Tag : Person is related to a tag via many to many relation. I also have a single dao for having multiple functions for querying db.

Solution 1:

I believe that your issue is how you are processing the UiModel list returned from the getFilteredModelList method.

Consider the following methods added for debugging purposes (in this example case, added to MainActivity.java). :-

public static void logUiModel(List<UiModel> uiModelList) {
    StringBuilder sb = new StringBuilder();
    for (UiModel u: uiModelList) {
        sb.append("\n\tUIModel Person ID = " + String.valueOf(u.getPerson().getPresonID()) + " Person Name = " + u.getPerson().getPersonName());
        for (Tag t: u.getTags()) {
            sb.append("\n\t\tTag ID =" + String.valueOf(t.getTagID()) + " Value = " + t.getTagValue());
        }
    }
    Log.d("UIMODELINFO(LIST)",sb.toString());
}

public static void logUiModel(UiModel uiModel) {
    StringBuilder sb = new StringBuilder().append("\nUIMODEL Person ID = " +String.valueOf(uiModel.getPerson().getPresonID()) + " Person Name = " + uiModel.getPerson().getPersonName());
    for (Tag t: uiModel.getTags()) {
        sb.append("\n\t\tTag ID =" + String.valueOf(t.getTagID()) + " Value = " + t.getTagValue());
    }
    Log.d("UIMODELINFO",sb.toString());
}

public static void logPersons(List<Person> personList) {
    StringBuilder sb = new StringBuilder().append("People are :-");
    for (Person p: personList) {
        sb.append("\n\tPerson ID is " + String.valueOf(p.getPresonID()) + " Name is" + p.getPersonName());
    }
    Log.d("PERSONINFO",sb.toString());
}

public static void logTags(List<Tag> tagList) {
    StringBuilder sb = new StringBuilder().append("Tags are");
    for (Tag t: tagList) {
        sb.append("\n\tTag ID is " + String.valueOf(t.getTagID()) + " Value is " + t.getTagValue());
    }
    Log.d("TAGINFO",sb.toString());
}

And the following changes to the getFilteredModelList method :-

@Transaction
public List<UiModel> getFilteredModelList(String tag) {
    List<UiModel> resultList = new ArrayList<>();
    long tagID = getTagIdByTagName(tag);
    Log.d("GETFLTRMDLLST","Processing TAG where value is " + tag + " ID is " + String.valueOf(tagID));
    List<Person> filteredPersonList = getAllPersonsForTagID(tagID);

    for (Person p : filteredPersonList) {
        Log.d("GETFLTRDMDLLST","Persons are :-");
        MainActivity.logPersons(filteredPersonList);
        List<Tag> associatedTags = getAllTagsForPerson(p.getPresonID());
        Log.d("GETFLTRDMDLLST","Tags are :-");
        MainActivity.logTags(associatedTags);
        UiModel u = new UiModel(p,associatedTags);
        Log.d("GETFLTRDMDLLST","UIModels are :-");
        MainActivity.logUiModel(u);
        resultList.add(u);
    }
    return resultList;
}

and the following testing code (again in MainActivity.java) :-

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mPDB = Room.databaseBuilder(this,PersonDatabase.class,"person.db").allowMainThreadQueries().build();
    addDataIfNone();
    List<Person> allPeople = mPDB.personDao().getAllPersons();
    logPersons(allPeople);
    List<Tag> allTags1 = mPDB.personDao().getAllTags();
    logTags(allTags1);
    List<PersonTagRelation> allPTRs = mPDB.personDao().getAllPersonTagRelations();
    logPersonTagRelations(allPTRs);
    List<UiModel> uiModelList = mPDB.personDao().getAllAsModelList();
    StringBuilder sb = new StringBuilder().append("UIMODELS are :-");
    for (UiModel u: uiModelList) {
        sb.append("\n\tPerson ID = " + String.valueOf(u.getPerson().getPresonID()) + " Name = " + u.getPerson().getPersonName());
        for (Tag t: u.getTagList()) {
            sb.append("\n\t\tTag ID = " + String.valueOf(t.getTagID()) + " Value = " + t.getTagValue());
        }
    }
    Log.d("UIMODELINFO",sb.toString());

    List<Tag> allTags = mPDB.personDao().getAllTags();
    List<UiModel> uiListOfModelsViaFilter;
    for (Tag t: allTags) {
        Log.d("TAGFILTERINF","Filtering with a TAG value of " + t.getTagValue() );
        uiListOfModelsViaFilter = mPDB.personDao().getFilteredModelList(String.valueOf(t.getTagValue()));
        logUiModel(uiListOfModelsViaFilter);
    }
}

private void addDataIfNone() {
    List<Person> people = mPDB.personDao().getAllPersons();
    if (people.size() > 0) return;

    Person[] peopleToAdd = new Person[]{
            new Person("A"),
            new Person("B"),
            new Person("C")
    };
    mPDB.personDao().insertNewPersons(peopleToAdd);

    Tag[] tagsToAdd = new Tag[] {
            new Tag("ai"),
            new Tag("ML"),
            new Tag("Flutter"),
            new Tag("JS"),
            new Tag("Kotlin"),
            new Tag("Dart")
    };
    mPDB.personDao().insertNewTags(tagsToAdd);

    PersonTagRelation[] ptr = new PersonTagRelation[]{
            new PersonTagRelation(1,1),
            new PersonTagRelation(1,2),
            new PersonTagRelation(2,1),
            new PersonTagRelation(3,3)
    };
    mPDB.personDao().insertNewRelations(ptr);
}

The the more pertinent out shows that for tag ai that persons A and B are extracted :-

06-24 11:27:53.365 9889-9889/? D/TAGFILTERINF: Filtering with a TAG value of ai
06-24 11:27:53.366 9889-9889/? D/GETFLTRMDLLST: Processing TAG where value is ai ID is 1
06-24 11:27:53.367 9889-9889/? D/GETFLTRDMDLLST: Persons are :-
06-24 11:27:53.367 9889-9889/? D/PERSONINFO: People are :-
        Person ID is 1 Name isA
        Person ID is 2 Name isB
06-24 11:27:53.367 9889-9889/? D/GETFLTRDMDLLST: Tags are :-
06-24 11:27:53.367 9889-9889/? D/TAGINFO: Tags are
        Tag ID is 1 Value is ai
        Tag ID is 2 Value is ML
06-24 11:27:53.367 9889-9889/? D/GETFLTRDMDLLST: UIModels are :-
06-24 11:27:53.367 9889-9889/? D/UIMODELINFO: UIMODEL Person ID = 1 Person Name = A
            Tag ID =1 Value = ai
            Tag ID =2 Value = ML
06-24 11:27:53.367 9889-9889/? D/GETFLTRDMDLLST: Persons are :-
06-24 11:27:53.367 9889-9889/? D/PERSONINFO: People are :-
        Person ID is 1 Name isA
        Person ID is 2 Name isB
06-24 11:27:53.367 9889-9889/? D/GETFLTRDMDLLST: Tags are :-
06-24 11:27:53.367 9889-9889/? D/TAGINFO: Tags are
        Tag ID is 1 Value is ai
06-24 11:27:53.367 9889-9889/? D/GETFLTRDMDLLST: UIModels are :-
06-24 11:27:53.367 9889-9889/? D/UIMODELINFO: UIMODEL Person ID = 2 Person Name = B
            Tag ID =1 Value = ai
06-24 11:27:53.369 9889-9889/? D/UIMODELINFO(LIST):     UIModel Person ID = 1 Person Name = A
            Tag ID =1 Value = ai
            Tag ID =2 Value = ML
        UIModel Person ID = 2 Person Name = B
            Tag ID =1 Value = ai 

Edit

This is the full PersonTagRelation class :-

@Entity(primaryKeys = {"personTagIDForPerson","personTagIDForTag"},
        foreignKeys = {
                @ForeignKey(entity = Person.class, parentColumns = "presonID", childColumns = "personTagIDForPerson"),
                @ForeignKey(entity = Tag.class, parentColumns = "tagID", childColumns = "personTagIDForTag"),
        })
public class PersonTagRelation {
    private long personTagIDForPerson, personTagIDForTag;

    public PersonTagRelation() {
    }

    public PersonTagRelation(long personTagIDForPerson, long personTagIDForTag) {
        this.personTagIDForPerson = personTagIDForPerson;
        this.personTagIDForTag = personTagIDForTag;
    }

    public long getPersonTagIDForPerson() {
        return personTagIDForPerson;
    }

    public void setPersonTagIDForPerson(long personTagIDForPerson) {
        this.personTagIDForPerson = personTagIDForPerson;
    }

    public long getPersonTagIDForTag() {
        return personTagIDForTag;
    }

    public void setPersonTagIDForTag(long personTagIDForTag) {
        this.personTagIDForTag = personTagIDForTag;
    }
}
  • Note the constructor that takes the two values (and how it is used in the addDataIfNone method).

Solution 2:

As it turns out , it was my silly ui at fault. I was generating a new id every time i add a tag, even if its already present. thus if my DB has the following entries(from what it looks like):

Person --- Tags
A      --- ai ,ml
B      --- ml
C      --- flutter

it was actually this :

id---person     id---tag        id_person-----------id_tag
101 -  A        11 - ai             101      --       11 
102 -  B        12 - ml             101      --       12
103 -  C        13 - ml //! wrong   102      --       13 //wrong!!  
                14 - flutter        103      --       14

My ui was not checking weather a tag existed before adding it as new tag. That's why the getFilteredModelList() function was not able to generate a list with more than 1 entry. so i ended up writing an extra checker code for that. Also, the dao ended up with the following mini change( an extra onConflict check for all three internal insertion functions):


@Dao
public abstract class PersonDao {
    private static final String TAG = "personDao>>";

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract void insertNewPerson(Person... p); 

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract void insertNewTag(Tag... t);          

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract void insertNewRelation(PersonTagRelation... relation);  

    ...
    }


Post a Comment for "Room Db With Many To Many Relations : Returns Single Entry On Querying For Filtered Data"