简体   繁体   中英

Primary and foreign key implementation Java and Android Room

This is my first time asking a question here in StackOverflow so forgive me if I am not asking the question right or a certain way.

I have two child classes (PerformanceAssessment and ObjectiveAssessment) from parent class Assessment. I was able to successfully do downcasting for the polymorphism part requirement of my school project, and I was able to save to their appropriate databases both instances of parent and child after downcasting (I have an assessment_table, performance_assessments, and objective_assessments table). However, I am now having Android Room database issues. I realized that I need to implement foreign keys to do the CRUD operations properly. How do I tweak my parent and child classes code and add proper annotations for implementing foreign and primary keys correctly? I need to have an autogenerated primary key for each class: parent (Assessment) and both children (PerformanceAssessment and ObjectiveAssessment). But I also need to utilize the children's primary key as a foreign key for the parent's databases so that when I delete an instance of Performance/Objective assessments, I can also delete the Assessment instance when I am downcasting. I found very little helpful information regarding this. Thanks in advance.

I am getting these errors right now:

Build Errors

Parent Class: Assessment.java

@Entity(tableName = "assessment_table")
public class Assessment {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "assessment_id")
    private final int assessment_id;

    private String assessmentName;
    private String assessmentStart;
    private String assessmentEnd;
    private int courseID;

    public Assessment(int assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, int courseID) {
        this.assessment_id = assessment_id;
        this.assessmentName = assessmentName;
        this.assessmentStart = assessmentStart;
        this.assessmentEnd = assessmentEnd;
        this.courseID = courseID;
    }

Child class: PerformanceAssessment.java

@Entity(tableName = "performance_assessment", foreignKeys = {
        @ForeignKey(
                entity = Assessment.class,
                parentColumns = "assessment_id",
                childColumns = "performance_id",
                onUpdate = CASCADE,
                onDelete = CASCADE
        )
})
public class PerformanceAssessment extends Assessment{

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "performance_id")
    private int performanceID;
    private String type;

    public PerformanceAssessment(int assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, int courseID, int performanceID, String type) {
        super(assessment_id, assessmentName, assessmentStart, assessmentEnd, courseID);
        this.performanceID = performanceID;
        this.type = type;
    }

Child class: ObjectiveAssessment.java

@Entity(tableName = "objective_assessment", foreignKeys = {
        @ForeignKey(
                entity = Assessment.class,
                parentColumns = "assessment_id",
                childColumns = "objective_id",
                onUpdate = CASCADE,
                onDelete = CASCADE
        )
})
public class ObjectiveAssessment extends Assessment{

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "objective_id")
    private int objective_ID;
    private String type;

    public ObjectiveAssessment(int assessmentID, String assessmentName, String assessmentStart, String assessmentEnd, int courseID, int objective_ID, String type) {
        super(assessmentID, assessmentName, assessmentStart, assessmentEnd, courseID);
        this.objective_ID = objective_ID;
        this.type = type;
    }

Here is the part where I am adding a new assessment in the app:

public void saveAssessment(View view) {

        assessmentTitle = editName.getText().toString();
        assessmentStart = editStart.getText().toString();
        assessmentEnd = editEnd.getText().toString();

        //Check if fields are empty:
        if (assessmentTitle.isEmpty() || assessmentStart.isEmpty() || assessmentEnd.isEmpty()) {

            Toast.makeText(AddAssessmentScreen.this, "Fill out required fields.", Toast.LENGTH_LONG).show();
            return;

        }
        else {

            //Check assessment type selected (used Downcasting for polymorphism):

            if (assessment_type == true) {
                Assessment performanceAssessment = new PerformanceAssessment(0, assessmentTitle, assessmentStart, assessmentEnd, currentCourseID, 0, selectedString);
                PerformanceAssessment castedPerformance = (PerformanceAssessment) performanceAssessment;
                //Insert assessment to database performance_assessment table (Performance child type):
                repository.insert(castedPerformance);
                Repository addToAssessment = new Repository(getApplication());
                //Insert assessment to database assessment_table (Assessment parent type):
                addToAssessment.insert(performanceAssessment);

            }
            else {
                Assessment objectiveAssessment = new ObjectiveAssessment(0,assessmentTitle, assessmentStart, assessmentEnd, currentCourseID, 0, selectedString);
                ObjectiveAssessment castedObjective = (ObjectiveAssessment) objectiveAssessment;
                //Insert assessment to database objective_assessment table (Objective child type):
                repository.insert(castedObjective);
                Repository addToAssessment = new Repository(getApplication());
                //Insert assessment to database assessment_table (Assessment parent type):
                addToAssessment.insert(objectiveAssessment);
            }

            Toast.makeText(AddAssessmentScreen.this, "New assessment added. Refresh previous screen.", Toast.LENGTH_LONG).show();

        }

How do I tweak my parent and child classes code and add proper annotations for implementing foreign and primary keys correctly?

Let the assessment_id always be the primary key ie don't code it in sub classes and then have a field for the reference/map/association with the parent in the subclasses.

So PerformanceAssessment could be:-

@Entity(tableName = "performance_assessment", foreignKeys = {
        @ForeignKey(
                entity = Assessment.class,
                parentColumns = "assessment_id",
                childColumns = "assessment_reference",
                onUpdate = CASCADE,
                onDelete = CASCADE
        )
})
public class PerformanceAssessment extends Assessment {

    private int assessment_reference; //<<<<<<<<<
    private String type;

    public PerformanceAssessment(int assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, int courseID, int assessment_reference, String type) {
        super(assessment_id, assessmentName, assessmentStart, assessmentEnd, courseID);
        this.assessment_reference = assessment_reference;
        this.type = type;
    }
    ....

and all compiles and the underlying tables will be build using:-

    _db.execSQL("CREATE TABLE IF NOT EXISTS `assessment_table` (`assessment_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `assessmentName` TEXT, `assessmentStart` TEXT, `assessmentEnd` TEXT, `courseID` INTEGER NOT NULL)");
    _db.execSQL("CREATE TABLE IF NOT EXISTS `performance_assessment` (`assessment_reference` INTEGER NOT NULL, `type` TEXT, `assessment_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `assessmentName` TEXT, `assessmentStart` TEXT, `assessmentEnd` TEXT, `courseID` INTEGER NOT NULL, FOREIGN KEY(`assessment_reference`) REFERENCES `assessment_table`(`assessment_id`) ON UPDATE CASCADE ON DELETE CASCADE )");
  • Obviously similar for the ObjectiveAssessment.

But I also need to utilize the children's primary key as a foreign key for the parent's databases so that when I delete an instance of Performance/Objective assessments, I can also delete the Assessment instance when I am downcasting.

What if, which is possible with the 1 (Assessment) - Many (PerformanceAssessments) , an Assessment has multiple PerformanceAssesments? Deleting 1 PerformanceAsessment would delete the parent Assessment, which would then, due to the use of onDelete CASCADE cascade the deletions to all the other PerformanceAssessments and ObjectiveAssessments. Deletions are not propagated up to the parent (that's why the term CASCADE is used as it implies downward rather than anyway).

As an example you have an assessment with 4 PerformanceAssessments and lets say 3 ObjectiveAssessments one of the PerformanceAssesments was wrongly added. Should everything have to be deleted and re-entered to correct the 1 wrong PeformanceAssessment.

You could introduce a Trigger to automate this upwards propogation, to delete anything and delete all, if that is what you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM