简体   繁体   中英

Associating tables using Room database in Android Studio

How can I associate two tables so that I can create a database that sort of matches the image attached.

表

I understand that the experiment ID, Date, Name has to be part of the parent table, with the Trial #, Variable, and Result be the child table associated to the parent table. However, I'm not very sure how to implement this in Room Database persistence using Entity and Dao. Can someone show me how?

I want the results to be that if the user selects the experiment in a recyclerview, it will be lead to another activity showing inputs of Variable and Result to add trials at the top while displaying the results of just that experiment below using a Recyclerview.

I believe the following Working Example is one of the ways that you can achieve what you want.

The Code

The Experiment Entity (table) Experiment.java

@Entity
public class Experiment {

    @PrimaryKey(autoGenerate = true)
    private long experimentId;
    private String experimentName;
    private String experimentDate;

    public Experiment() {
    }

    @Ignore
    public Experiment(String name, String date) {
        this.experimentName = name;
        this.experimentDate = date;
    }

    public long getExperimentId() {
        return experimentId;
    }

    public void setExperimentId(long experimentId) {
        this.experimentId = experimentId;
    }

    public String getExperimentName() {
        return experimentName;
    }

    public void setExperimentName(String experimentName) {
        this.experimentName = experimentName;
    }

    public String getExperimentDate() {
        return experimentDate;
    }

    public void setExperimentDate(String experimentDate) {
        this.experimentDate = experimentDate;
    }
}
  • Nothing special except perhaps the @Ignore 'd constructor (for convenience)

The Trial Entity Trial.java

@Entity
public class Trial {

    @PrimaryKey(autoGenerate = true)
    private long trialId;
    @ForeignKey(entity = Experiment.class, parentColumns = {BaseColumns._ID},childColumns = "parentExperiment", onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE)
    private long parentExperiment;
    private String trialVariable;
    private String trialResult;

    public Trial() {
    }

    @Ignore
    public Trial(long parentExperimentId, String variable, String result) {
        this.parentExperiment = parentExperimentId;
        this.trialVariable = variable;
        this.trialResult = result;
    }

    public long getTrialId() {
        return trialId;
    }

    public void setTrialId(long trialId) {
        this.trialId = trialId;
    }

    public long getParentExperiment() {
        return parentExperiment;
    }

    public void setParentExperiment(long parentExperiment) {
        this.parentExperiment = parentExperiment;
    }

    public String getTrialVariable() {
        return trialVariable;
    }

    public void setTrialVariable(String trialVariable) {
        this.trialVariable = trialVariable;
    }

    public String getTrialResult() {
        return trialResult;
    }

    public void setTrialResult(String trialResult) {
        this.trialResult = trialResult;
    }
}
  • Nothing special except perhaps the @Ignore 'd constructor (for convenience)

Dao.java (combined for convenience)

public interface Dao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long[] insertExperiments(Experiment... experiments);

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long insertExperiment(Experiment experiment);

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long[] insertTrials(Trial... trials);

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long insertTrial(Trial trial);

    @Update(onConflict = OnConflictStrategy.IGNORE)
    int updateExperiments(Experiment... experiments);

    @Update(onConflict = OnConflictStrategy.IGNORE)
    int updateExperiment(Experiment experiment);

    @Update(onConflict = OnConflictStrategy.IGNORE)
    int updateTrials(Trial... trials);

    @Update(onConflict = OnConflictStrategy.IGNORE)
    int updateTrial(Trial trial);

    @Delete
    int deleteExperiments(Experiment... experiments);

    @Delete
    int deleteExperiment(Experiment experiment);

    @Delete
    int deleteTrials(Trial... trials);

    @Delete
    int deleteTrial(Trial trial);

    @Query("SELECT * FROM Experiment")
    List<Experiment> getAllexperiments();

    @Query("SELECT * FROM Experiment WHERE experimentDate BETWEEN :startdate AND :enddate")
    List<Experiment> getExperimentsInDateRange(String startdate, String enddate);

    @Query("SELECT * FROM Trial")
    List<Trial> getAllTrials();

    @Query("SELECT * FROM Experiment JOIN Trial ON parentExperiment = experimentId")
    List<TrialWithExperiment> getExperimentsWithTrials();


    public class TrialWithExperiment {
        private long experimentId;
        private String experimentName;
        private String experimentDate;
        private long trialId;
        private String trialVariable;
        private String trialResult;

        public long getExperimentId() {
            return experimentId;
        }

        public void setExperimentId(long experimentId) {
            this.experimentId = experimentId;
        }

        public String getExperimentName() {
            return experimentName;
        }

        public void setExperimentName(String experimentName) {
            this.experimentName = experimentName;
        }

        public String getExperimentDate() {
            return experimentDate;
        }

        public void setExperimentDate(String experimentDate) {
            this.experimentDate = experimentDate;
        }

        public void setTrialId(long trialId) {
            this.trialId = trialId;
        }

        public long getTrialId() {
            return trialId;
        }

        public String getTrialVariable() {
            return trialVariable;
        }

        public void setTrialVariable(String trialVariable) {
            this.trialVariable = trialVariable;
        }

        public String getTrialResult() {
            return trialResult;
        }

        public void setTrialResult(String trialResult) {
            this.trialResult = trialResult;
        }
    }
}
  • Note the TrialWithExperiment class, this defines a combination of a Trial and it's owning Experiment.
  • Note how column names are distinct eg no just id but experimentId and trialId differentiates them.
  • Note the last @Query getExperimentsWithTrials() this will return a list of Trials with their experiments.

The @Database ExperimentDatabase.java

@Database(entities = {Experiment.class, Trial.class}, version = 1,exportSchema = false)
public abstract class ExperimentDatabase extends RoomDatabase {
    public abstract Dao getDao();
} 

Finally an activity that utilises the above :-

public class MainActivity extends AppCompatActivity {

    ExperimentDatabase mDB;

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

        mDB = Room.databaseBuilder(this,ExperimentDatabase.class,"experimentdb").allowMainThreadQueries().build();
        mDB.getDao().insertExperiments(new Experiment[]{
                new Experiment("Experiment 1","2019-01-01"),
                new Experiment("Experiment 2", "2019-02-01")
        });
        List<Experiment> experiments = mDB.getDao().getExperimentsInDateRange("2019-01-01","2019-12-31");
        for (Experiment e: experiments) {
            Log.d("EXPERIMENTS", "Experiment is " + e.getExperimentName() + " on " + e.getExperimentDate());
        }

        experiments = mDB.getDao().getAllexperiments();
        for (Experiment e: experiments) {
            Log.d("EXPERIMENTS", "Experiment is " + e.getExperimentName() + " on " + e.getExperimentDate());
        }

        for (Experiment e: experiments) {
            mDB.getDao().insertTrial(
                    new Trial(
                            e.getExperimentId(),
                            "Variable for " + e.getExperimentName(),
                            "Result for Experiment on " + e.getExperimentDate()
                    )
            );
        }

        List<Trial> trials = mDB.getDao().getAllTrials();
        for (Trial t: trials ) {
            Log.d("TRIAL ",
                    "Trial is for ExperimentID " + String.valueOf(t.getParentExperiment()) +
                            "\n\tVariable = " + t.getTrialVariable() +
                            "Result = " + t.getTrialResult()
            );
        }


        List<Dao.TrialWithExperiment> trialsWithExperiments = mDB.getDao().getExperimentsWithTrials();

        for (Dao.TrialWithExperiment te:trialsWithExperiments) {
            Log.d(
                    "TRIALWITHEXPERIMENT",
                    "Experiment Name = " + te.getExperimentName() +
                            "\n\tExperiment Date = " + te.getExperimentDate() +
                            "\n\t\tTrial Variable = " + te.getTrialVariable() +
                            "\n\t\tTrial Result = " + te.getTrialResult()
            );

        }
    }
}

This :-

  • Adds 2 experiments (every run)
  • Retrieves Experiments in a date range (this assumes an SQLite recognised date format) and outputs them to the log.
  • Retrieves all Experiments and outputs them to the log.
  • Uses the List of experiments to add a Trial to each experiment.
  • Retrieves all Trials and outputs them to the log.
  • Retrieves all Trials along with the parent Experiment and outputs them to the log.

The Result

 05-28 10:19:42.770 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 1 on 2019-01-01 05-28 10:19:42.770 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 2 on 2019-02-01 05-28 10:19:42.776 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 1 on 2019-01-01 05-28 10:19:42.776 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 2 on 2019-02-01 05-28 10:19:42.786 5750-5750/? D/TRIAL: Trial is for ExperimentID 1 Variable = Variable for Experiment 1Result = Result for Experiment on 2019-01-01 05-28 10:19:42.786 5750-5750/? D/TRIAL: Trial is for ExperimentID 2 Variable = Variable for Experiment 2Result = Result for Experiment on 2019-02-01 05-28 10:19:42.787 5750-5750/? D/TRIALWITHEXPERIMENT: Experiment Name = Experiment 1 Experiment Date = 2019-01-01 Trial Variable = Variable for Experiment 1 Trial Result = Result for Experiment on 2019-01-01 05-28 10:19:42.787 5750-5750/? D/TRIALWITHEXPERIMENT: Experiment Name = Experiment 2 Experiment Date = 2019-02-01 Trial Variable = Variable for Experiment 2 Trial Result = Result for Experiment on 2019-02-01 

Alternative/Additional

Another approach could be to utilise @Relation to create an object per Experiment that includes a list of the related/associated Trial(s) in that object.

Expanding upon the above then the following could be added to Dao.java

@Query("SELECT * FROM  Experiment")
List<ExperimentWithTrials> getExperimentsAndTrials();

class ExperimentWithTrials {
    private long experimentId;
    private String experimentName;
    private String experimentDate;
    @Relation(parentColumn = "experimentId", entityColumn = "parentExperiment")
    List<Trial> trials;

    public long getExperimentId() {
        return experimentId;
    }

    public void setExperimentId(long experimentId) {
        this.experimentId = experimentId;
    }

    public String getExperimentName() {
        return experimentName;
    }

    public void setExperimentName(String experimentName) {
        this.experimentName = experimentName;
    }

    public String getExperimentDate() {
        return experimentDate;
    }

    public void setExperimentDate(String experimentDate) {
        this.experimentDate = experimentDate;
    }

    public List<Trial> getTrials() {
        return trials;
    }

    public void setTrials(List<Trial> trials) {
        this.trials = trials;
    }
}

and then the following could be added to MainActivity.java

    List<Dao.ExperimentWithTrials> experimentsWithTrials = mDB.getDao().getExperimentsAndTrials();
    for (Dao.ExperimentWithTrials et: experimentsWithTrials ) {
        Log.d(
                "EXPERIMENTANDTRIALS",
                "Experiment Name = " + et.getExperimentName() +
                        "\n\tExperiment Date = " + et.getExperimentDate()
        );
        for (Trial t: et.getTrials()) {
            Log.d(
                    "TRIALFOREXPERIMENT",
                    "\t\tVariable = " + t.getTrialVariable() +
                            "\n\t\tResult = " + t.getTrialResult()
            );
        }
    }
}
  • Note how the Trials are obtained via the looping through the list of Trials embedded with the ExperimentWithTrials object, as opposed to the list of combined Experiment and Trial data from the previous.

    • This probably to purer OO way.
    • However, SQLite wise this appears to be cumbersome/inefficient as it appears to run multiple queries. One to get the Experiments and then another to get the underlying Trials for each experiment.

Resultant additional output

 05-28 13:05:35.052 6524-6524/? D/EXPERIMENTANDTRIALS: Experiment Name = Experiment 1 Experiment Date = 2019-01-01 05-28 13:05:35.052 6524-6524/? D/TRIALFOREXPERIMENT: Variable = Variable for Experiment 1 Result = Result for Experiment on 2019-01-01 05-28 13:05:35.052 6524-6524/? D/EXPERIMENTANDTRIALS: Experiment Name = Experiment 2 Experiment Date = 2019-02-01 05-28 13:05:35.052 6524-6524/? D/TRIALFOREXPERIMENT: Variable = Variable for Experiment 2 Result = Result for Experiment on 2019-02-01 
  • Note that for convenience all of the above has been run on the main thread (ie .allowMainThreadQueries() has been used). If you follow recommendations the all database access would be via another thread, in which case the @Transaction annotation is advised for queries.

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