Files
notes/docs/lectures/mdp/11_room_mvvm.md
T

5.5 KiB
Raw Blame History

Room and MVVM

ROOM

Room Persistence Library

  • An architecture component
  • A database layer on top of SQLite
    • Takes care of tasks that SQLiteHelper dealt with
      • Like compile-time validation of SQL queries
    • Allows us to think about objects with fields rather than tables with rows

Room Components

image-20220107154928136

Database
  • Contains a database holder
  • A singleton serves as the main access point to persistent data
Entities
  • Java classes that represent (or become) tables in the database
DAO
  • Data Access Object (software design pattern)
  • An abstract interface for accessing the database to retrieve a given entity

Database

An abstract class that

  • Is annotated with @Database
  • Extends RoomDatabase
  • Lists the entities associated with the database
  • Includes abstract methods that return DAO interfaces for the entities

Is generally implemented as a singleton to retrieve a reference to the database with the application context

  • Returned by RoomDatabase.databaseBuilder

A more structured approach to the database lifecycle

  • onCreate, onOpen, onDestructiveMigration
  • When the database schema is modified, we must define a migration strategy

Room does not support database access on the main thread

  • Queries could take a while, so they shouldn’t be done on the main/UI thread

Entity

  • Plain old Java class
    • Represents a table in the underlying database
    • Sets of related fields
  • Annotated to describe how to think about it in terms of the database
    • Needs to have a @PrimaryKey
    • Field names become column names, unless we annotate an alternate
      • @ColumnInfo(name = 'my_column_name')
    • @Ignore certain fields
  • Embedded entities, 1-to-1 or 1-to-many relationships
    • @Embedded, @Relation, @Transaction

DAOs

  • Interface / Abstract class
    • Common practice to have one DAO per entity
  • Implementation is created at compile time

Specify convenience queries, annotated to tell Room what to do

  • @Insert generates an implementation that inserts all parameters into the database in a single transaction.
  • @Update updates a set of entities given as parameters using a query that matches against the primary key of each entity.
    • If the entity is updated, it will update the underlying SQL database
  • @Query read/write operations as something like a conventional database query.
  • @Delete removes entities given as parameters.

MVVM Architecture

image-20220107160502350

VIEW (UI controller) is the V

ViewModel is the current state of the data needed to populate the view

Room is the model; it is all the data for the entire application

ViewModel \subset (is a subset of) Room Database

NOTE: Room is just the database manager, others exist.

Repository - as far as the view model is concerned, it asks the repository for all data. It abstracts access to different possible back-ends. The view model doesn’t need to know how the data is stored, e.g. whether data is stored on data/.cache or on a web server ftp://192.168.0.5.

The repository is responsible for connecting to the website and downloading data into cache to support functionality when the internet drops out.

All view models get their data from the same repository

Coding Example

import androidx.room.Entity
    
@Entity(tableName = "myTable")
public class Record {
    
    @PrimaryKey
    @NonNull
    @ColumnInfo(name = "name") // only needed if they don't match
    
    public Record(@NonNull String name, String colour) {
        this.name = name;
        ...
    }
}

Interface DAO

//Can be an abstract class or interface
//Room will generate the implementation based on this

import androidx.room.Doa;

@Dao
public interface RecordDao {
    @Insert
    void insert (Record r);
    
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    void insertLots (Record... r);
    
    @Query("DELETE FROM myTable")
    void deleteALl();
    
    @Query("SELECT * FROM myTable ORDER BY name ASC")
    List<Record> getAlphaRecord();
    
    @Query("SELECT * FROM myTable")
    LiveData<List<Record>> getAlphaRecord();
    //LiveData is lifecycle aware
}

Boilerplate database stuff

import androidx.room.RoomDatabase;

@Database(entities = {Record.class}, version=1, exportScheme=false)
public abstract class myDatabase extends RoomDatabase {
    
    public abstract RecordDao();
    
    private static volatile myDatabase INSTANCE;
    
    //as we're not allowed to query on the main thread
    static final ExecutorService dbWriter = Executors.newFixedThreadPool(4);
    
    //context needed to know file path
    static myDatabase getDatabse(final Context context) {
        
        if (INSTANCE == null) {
            synchronised (myDatabase.class) {
                INSTANCE = Room.databaseBuilder(
                    context.getApplicationContext(),
					myDatabase.class, "myDatabase")
                    .fallbackToDestructiveMigration()
                    .build();
            }
                
        }
    }
}

Main Activity

public class MainActivity extends .. {
    
    myDatabase db;
    RecordDao rDao;
    
    protected onCreate (...){
        db = myDatabase(getApplicationContext());
        rDao = db.recordDao();
    }
    
    public void addNewRecord(){
        myDatabase.dbWriter.execute(() -> {
            Record r = new Record ("data", "data");
            rDao.insert(r);
        })
    }
}