Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. One-to-many relationship exists when one row in table A may be linked with many rows in table B, but one row in table B is linked to only one row in table A.
How works room?
The importance of using room to work classes as entities is very powerful but its documentation is very poor on how to work the relations between entities.
To know the basic notions of room you can follow this tutorial https://developer.android.com/training/data-storage/room
Android one to many in room explanation:
For this example using basic concepts of databases and room, first declare their entities Course.java and Student.java:
@Entity(tableName = "course")
public class Course {
@PrimaryKey(autoGenerate = true)
private long id_course;
private String courseName;
public long getId_course() {
return id_course;
}
public void setId_course(long id_course) {
this.id_course = id_course;
}
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public Course(String courseName) {
this.courseName = courseName;
}
}
Course.java is our parent class and we add an id that is auto generated with the @PrimaryKey annotation (autoGenerate = true)
@Entity(tableName = "student")
public class Student {
@PrimaryKey(autoGenerate = true)
private long id_student;
@ForeignKey
(entity = Course.class,
parentColumns = "id_course",
childColumns = "id_fkcourse",
onDelete = CASCADE
)
private long id_fkcourse;
private String studentName;
public long getId_student() {
return id_student;
}
public Student(String studentName) {
this.studentName = studentName;
}
public void setId_student(long id_student) {
this.id_student = id_student;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public long getId_fkcourse() {
return id_fkcourse;
}
public void setId_fkcourse(long id_course) {
this.id_fkcourse = id_course;
}
In the child class Student.java an attribute must be added that contains the id of the parent class, in this case id_fkcourse and the annotation @ForeigKey will be used to make the relationship between entities.
/*** EXCLUSIVE CLASS TO MANAGE ONE TO MANY IN ROOM ***/
public class CourseWithStudents {
@Embedded
public Course course;
@Relation(
parentColumn = "id_course",
entityColumn = "id_student"
)
public List<Student> students;
public CourseWithStudents(Course course, List<Student> students) {
this.course = course;
this.students = students;
}
}
To do this, create a new data class where each instance holds an instance of the parent entity and a list of all corresponding child entity instances. Add the @Relation annotation to the instance of the child entity, with parentColumn set to the name of the primary key column of the parent entity and entityColumn set to the name of the column of the child entity that references the parent entity's primary key.
Add Dao interface:
@Dao
public interface CourseDao {
@Transaction
@Insert
long insertCourse(Course course);
@Insert
void insertStudents(List<Student> students);
}
It’s important to mention that the @Transaccion annotation should be used. One method will be for the course and another for the list of assigned students.
Continuing with the MVVM pattern we already have to have our classes for ViewModel and Repository:
public class CourseRepository {
private CourseDao courseDao;
public CourseRepository(Application application) {
Database database = Database.getDatabase(application);
courseDao = database.courseDao();
}
public void insert(CourseWithStudents courseWithStudents) {
new insertAsync(courseDao).execute(courseWithStudents);
}
private static class insertAsync extends AsyncTask<CourseWithStudents, Void, Void> {
private CourseDao courseDaoAsync;
insertAsync(CourseDao courseDao) {
courseDaoAsync = courseDao;
}
@Override
protected Void doInBackground(CourseWithStudents... courseWithStudents) {
long identifier = courseDaoAsync.insertCourse(courseWithStudents[0].course);
for (Student student : courseWithStudents[0].students) {
student.setId_fkcourse(identifier);
}
courseDaoAsync.insertStudents(courseWithStudents[0].students);
return null;
}
}
}
In order for the inserting process not to conflict with the main thread we must add an asynchronous class that handles it.
In the for instruction we assign each student the id of the course that was inserted. This id is generated automatically and we only have to obtain it by assigning a long data type to the result.
This snippet is very important what understand because manage the relationship.
Viewmodel class:
public class CourseViewModel extends AndroidViewModel {
public CourseViewModel(@NonNull Application application) {
super(application);
courseRepository = new CourseRepository(application);
}
private CourseRepository courseRepository;
public void insertCourseWithStudents(CourseWithStudents courseWithStudents){
courseRepository.insert(courseWithStudents);
}
}
Finally these are the classes that are in charge of managing the relationship of one to many. The CourseWithStudents class is very important since it handles the relationship at the entity level linking both course models with their students.
Sqlite looks like:
Top comments (1)
You have an error in the 'EXCLUSIVE CLASS', where entityColumn should be 'id_fkcourse'. You explained it in words, just didn't put in code. Thanks for the help.