6

I am developing an android application and am using the new Architecture Components in Android OS: LiveData, ViewModel and Room. I have a small problem with the Room implementation about creating a @Relation which returns the result from a JOIN query (many-to-many relationship).

My DB structure looks like this:

@Entity
public class Student{
  @PrimaryKey
  private int id;
  private String name;
  private String email;
} 

@Entity
public class Group{
  @PrimaryKey
  private int id;
  private String name;
}

@Entity(foreignKeys = {
            @ForeignKey(entity = Student.class,
                    parentColumns = "id",
                    childColumns = "student_id"),
            @ForeignKey(entity = Group.class,
                    parentColumns = "id",
                    childColumns = "group_id")
    })
public class StudentGroup{

  private int studentId;
  private int groupId;
}

How I can get all Groups only for a specific student, something like this?

public class StudentWithGroups{
  @Relation(parentColumn = "id", entityColumn = "rule_id", entity = 
StudentGroup.class)
  private List<Group> groups;
}

I already checked questions like How can I represent a many to many relation with Android Room? and Android Persistence room: "Cannot figure out how to read this field from a cursor"

iron9
  • 397
  • 2
  • 12
MrVasilev
  • 1,503
  • 2
  • 17
  • 34

2 Answers2

6

With the introduction to Junction in room you can handle many-to-many relationship with ease.

Modify primary key of student and group tables as:

@Entity
public class Student{
  @PrimaryKey
  private int sId;
  private String name;
  private String email;
} 

@Entity
public class Group{
  @PrimaryKey
  private int gId;
  private String name;
}

@Entity(foreignKeys = {
        @ForeignKey(
                entity = Student.class,
                parentColumns = "sId",
                childColumns = "studentId"
        ),
        @ForeignKey(
                entity = Group.class,
                parentColumns = "gId",
                childColumns = "groupId"
        )
})
public class StudentGroup{
  private int studentId;
  private int groupId;
}

You can get all groups of specific student as:

 public class StudentWithGroups{
   @Embedded
   Student student;
   @Relation(
         parentColumn = "sId",
         entity = Group.class,
         entityColumn = "gId",
         associateBy = @Junction(
                 value = StudentGroup.class,
                 parentColumn = "studentId",
                 entityColumn = "groupId"
         )
   )
   List<Group> groups;
 }

Now you can query database for the result as:

 @Dao
 public interface StudentDao {
   @Query("SELECT * FROM Student")
   List<StudentWithGroups> getGroupsOfStudent();
}
ph_0
  • 617
  • 8
  • 27
Nischal
  • 870
  • 1
  • 10
  • 16
  • 1
    This is now outdated and does not compile using latest Room library. Please refer to official doc on an updated and simplified many-to-many relationship setup: https://developer.android.com/training/data-storage/room/relationships#many-to-many – jschlepp Feb 24 '22 at 23:41
2

How I can get all Groups only for specific student, something like that?

In this sample code, I have:

  @Query("SELECT categories.* FROM categories\n"+
    "INNER JOIN customer_category_join ON categories.id=customer_category_join.categoryId\n"+
    "WHERE customer_category_join.customerId=:customerId")
List<Category> categoriesForCustomer(String customerId);

Translating that into your entities results in something like:

  @Query("SELECT Group.* FROM Group\n"+
    "INNER JOIN StudentGroup ON Group.id=StudentGroup.groupId\n"+
    "WHERE StudentGroup.studentId=:studentId")
List<Group> groupsForStudent(String studentId);

In general, with Room, work out the SQL ignoring Room, then use that SQL in your DAO.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    Yes, I can do it with Query which to use some kind of JOIN but I wondering if there is a way to put that result from this Query into the class that keep the Relation-s. What I mean, want to get the whole Object from some table with all of its relationships data. For example if I have tables Students, Groups and StudentGroup I want the StudentDao to return all data from Studets table with list of Group object. Hope explain it well :) – MrVasilev Feb 07 '18 at 11:10
  • @MrVasilev: I do not think that is possible with today's Room. – CommonsWare Feb 07 '18 at 11:48
  • Thanks. My research ends with the same answer. Hope in the next version that would be added – MrVasilev Feb 08 '18 at 08:58
  • Have you created a feature requests for Room already? I think it would be a really good-to-have feature. Even if the internal implementation would make use of two different queries in a transaction, you'd be able to save a lot of code and time if Room were able to detect when a "join" entity and a full M:N could be used in a @Relation field. Something like @Relation( [...] joinEntity=StudentGroups.class, joinEntityColumn="studentId") – fast3r Mar 14 '18 at 15:02
  • @fast3r: "Have you created a feature requests for Room already?" -- not for this, sorry. – CommonsWare Mar 14 '18 at 15:16