Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting started with ROOM

Getting started with ROOM

Slide to my talk at Devfest17.

Room is a persistence library. It is one of the architecture components. It is a wrapper around SQLite.

Omolara Adejuwon

November 18, 2017
Tweet

More Decks by Omolara Adejuwon

Other Decks in Programming

Transcript

  1. NO ROOM FOR DOUBT
    Getting Started With ROOM
    #DevFest17 #DevFestSW

    View Slide

  2. Omolara Adejuwon
    (@_larikraun)
    Android, iOS @ LawPavilion
    #DevFest17 #DevFestSW

    View Slide

  3. Room is a persistence library that provides
    an abstraction layer over SQLite.

    View Slide

  4. Features
    ● Wrapper around SQLite
    ● Eliminates boilerplate code
    ● Upgrade database versions with ease - MIGRATION
    ● Enforces you don’t perform database operations on the main
    thread
    ● Checks query at compile time
    ● Testing is easier

    View Slide

  5. Features
    ● Wrapper around SQLite
    ● Eliminates boilerplate code
    ● Upgrade database versions with ease - MIGRATION
    ● Enforces you don’t perform database operations on the main
    thread
    ● Checks query at compile time
    ● Testing is easier

    View Slide

  6. Features
    ● Wrapper around SQLite
    ● Eliminates boilerplate code
    ● Upgrade database versions with ease - MIGRATION
    ● Enforces you don’t perform database operations on the main
    thread
    ● Checks query at compile time
    ● Testing is easier

    View Slide

  7. Features
    ● Wrapper around SQLite
    ● Eliminates boilerplate code
    ● Upgrade database versions with ease - MIGRATION
    ● Enforces you don’t perform database operations on the main
    thread
    ● Checks query at compile time
    ● Testing is easier

    View Slide

  8. Features
    ● Wrapper around SQLite
    ● Eliminates boilerplate code
    ● Upgrade database versions with ease - MIGRATION
    ● Enforces you don’t perform database operations on the main
    thread
    ● Checks query at compile time
    ● Testing is easier

    View Slide

  9. Features
    ● Wrapper around SQLite
    ● Eliminates boilerplate code
    ● Upgrade database versions with ease - MIGRATION
    ● Enforces you don’t perform database operations on the main
    thread
    ● Checks query at compile time
    ● Testing is easier

    View Slide

  10. Major Components
    ● @Entity - Defines table structure
    ● @DAO - An interface or abstract class. The functions define how to
    access the database
    ● @Database - Connects all the pieces of Room together

    View Slide

  11. Let’s Get Started

    View Slide

  12. Update project level gradle file
    buildscript {
    repositories {
    google()
    ...
    }
    ...
    }

    View Slide

  13. Update app level gradle file
    dependencies {
    ...
    implementation "android.arch.persistence.room:runtime:1.0.0"
    annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
    ...
    }

    View Slide

  14. Basic POJO
    public class Attendee {
    private int id;
    private String name;
    private String email;
    private String category;
    private String extraFieldWeWantToIgnore;
    //other Getters & Setters
    }
    id name email category
    1 Omolara [email protected] Speaker
    ... ... ... ...
    Attendee Table

    View Slide

  15. Create Entity
    @Entity (tableName="Attendee")
    public class Attendee {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    private int id;
    @ColumnInfo(name = "name")
    private String name;
    @ColumnInfo(name = "email")
    private String email;
    private String category;
    @Ignore
    private String extraFieldWeWantToIgnore;
    //other Getters & Setters
    }
    id name email category
    1 Omolara [email protected] Speaker
    ... ... ... ...
    Attendee Table

    View Slide

  16. Create Entity
    @Entity (tableName="Attendee")
    public class Attendee {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    private int id;
    @ColumnInfo(name = "name")
    private String name;
    @ColumnInfo(name = "email")
    private String email;
    private String category;
    @Ignore
    private String extraFieldWeWantToIgnore;
    //other Getters & Setters
    }
    id name email category
    1 Omolara [email protected] Speaker
    ... ... ... ...
    Attendee Table

    View Slide

  17. Create Entity
    @Entity (tableName="Attendee")
    public class Attendee {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    private int id;
    @ColumnInfo(name = "name")
    private String name;
    @ColumnInfo(name = "email")
    private String email;
    private String category;
    @Ignore
    private String extraFieldWeWantToIgnore;
    //other Getters & Setters
    }
    id name email category
    1 Omolara [email protected] Speaker
    ... ... ... ...
    Attendee Table

    View Slide

  18. Create Entity
    @Entity (tableName="Attendee")
    public class Attendee {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    private int id;
    @ColumnInfo(name = "name")
    private String name;
    @ColumnInfo(name = "email")
    private String email;
    private String category;
    @Ignore
    private String extraFieldWeWantToIgnore;
    //other Getters & Setters
    }
    id name email category
    1 Omolara [email protected] Speaker
    ... ... ... ...
    Attendee Table

    View Slide

  19. Create the DAO
    @Dao
    public interface AttendeeDao {
    @Insert/@delete/@update
    void insert/delete/update (Attendee attendee);
    }
    Annotations for the win

    View Slide

  20. Create the DAO
    @Dao
    public interface AttendeeDao {
    @Insert/@delete/@update
    void insert/delete/update (Attendee attendee);
    @Query("Select * from attendee where email=:email")
    Attendee getAttendedByEmail (String email);
    }
    Parameter Binding

    View Slide

  21. Create the DAO
    @Dao
    public interface AttendeeDao {
    @Insert/@delete/@update
    void insert/delete/update (Attendee attendee);
    @Query("Select * from attendee where email=:email")
    Attendee getAttendedByEmail (String email);
    @Query("Select * from attendee")
    List getAllAttendees ();
    }
    Return a list

    View Slide

  22. Create the DAO
    @Dao
    public interface AttendeeDao {
    @Query("Select * from attendeeess where email=:email")
    Attendee getAttendedByEmail (String email);
    }
    Compile Time Error

    View Slide

  23. Create the database class
    @Database(entities = {Attendee.class}, version = 1)
    public abstract class AppDatabase extends RoomDatabase {
    public abstract AttendeeDao attendeeDao ();
    }

    View Slide

  24. Get the database instance
    public static AppDatabase database;
    database = Room.databaseBuilder (this, AppDatabase.class, "database_name")
    .build ();
    CAUTION!!!
    Recommended to keep it a singleton ‘cos database calls are not cheap

    View Slide

  25. Access the DB - Insert new record
    Attendee attendee = new Attendee (1, "Omolara Adejuwon",
    "[email protected]", "Speaker");
    RoomApp.database.attendeeDao ().insert (attendee);

    View Slide

  26. Access the DB - Retrieve All Attendees
    List attendees = RoomApp.database.attendeeDao ().getAllAttendees ();

    View Slide

  27. Access the DB - Retrieve Attendees By Email
    Attendee attendee = RoomApp.database.attendeeDao ()
    .getAttendeesByEmail ("[email protected]");

    View Slide

  28. Migration

    View Slide

  29. defines the actions that should be performed when migrating
    from one specific version to another
    Migration
    1 2 3

    View Slide

  30. Scenarios and actions
    ● Modify schema, no change in version - IllegalStateException

    View Slide

  31. Scenarios and actions
    ● Modify schema, no change in version - IllegalStateException
    ● Increase version, no provision for migration - IllegalStateException

    View Slide

  32. Scenarios and actions
    ● Modify schema, no change in version - IllegalStateException
    ● Increase version, no provision for migration - IllegalStateException
    ● Increase version, enable fallback to destructive migration - wipes data

    View Slide

  33. Scenarios and actions
    ● Modify schema, no change in version - IllegalStateException
    ● Increase version, no provision for migration - IllegalStateException
    ● Increase version, enable fallback to destructive migration - wipes data
    ● Increase version, provide migration - preserves data

    View Slide

  34. Fallback to destructive migration?
    database = Room.databaseBuilder (this, AppDatabase.class, "database_name")
    .fallbackToDestructiveMigration ()
    .build ();
    Wipe the data.
    I don’t care

    View Slide

  35. Modify Schema, Provide Migration
    @Database(entities = {Attendee.class}, version = 2)
    public abstract class AppDatabase extends RoomDatabase {
    public abstract AttendeeDao attendeeDao ();
    }
    Change version number

    View Slide

  36. Modify Schema, Provide Migration
    public static final Migration MIGRATION_1_2 = new Migration (1, 2) {
    @Override
    public void migrate (@NonNull SupportSQLiteDatabase database) {
    database.execSQL ("ALTER TABLE attendee ADD COLUMN phone_number
    TEXT");
    }
    };
    -------------------------------------------------------------------------
    database = Room.databaseBuilder (this, AppDatabase.class, "devfestsw-java.db")
    .addMigrations (AppDatabase.MIGRATION_1_2)
    .build ();

    View Slide

  37. Testing

    View Slide

  38. Testing DAOs - Initialize DB
    @Before
    public void initDb () {
    database = Room.inMemoryDatabaseBuilder (InstrumentationRegistry.getContext (),
    AppDatabase.class).build ();
    }

    View Slide

  39. Testing DAOs - Insert new record
    @Test
    public void insert_retrieveAll_hasCorrectSize () throws Exception {
    database.attendeeDao ().insert (ATTENDEE);
    }

    View Slide

  40. Testing DAOs - Retrieve all records
    @Test
    public void insert_retrieveAll_hasCorrectSize () throws Exception {
    database.attendeeDao ().insert (ATTENDEE);
    List attendees = database.attendeeDao ().getAllAttendees ();
    }

    View Slide

  41. Testing DAOs - Verify records
    @Test
    public void insert_retrieveAll_hasCorrectSize () throws Exception {
    database.attendeeDao ().insert (ATTENDEE);
    List attendees = database.attendeeDao ().getAllAttendees ();
    assertEquals (1, attendees.size ());
    assertEquals (ATTENDEE.getName (), attendees.get (0).getName ());
    }

    View Slide

  42. Testing DAOs - Close DB
    @After
    public void closeDb () {
    database.close ();
    }

    View Slide

  43. Testing Migrations - Define Schema Location
    defaultConfig {
    ...
    javaCompileOptions {
    annotationProcessorOptions {
    arguments = ["room.schemaLocation":
    "$projectDir/schemas".toString()]
    }
    }
    }

    View Slide

  44. Testing Migrations - Add schema location to
    source sets
    android {
    ...
    sourceSets {
    androidTest.assets.srcDirs +=
    files("$projectDir/schemas".toString())
    }
    }

    View Slide

  45. Testing Migrations - Update build.gradle file
    dependencies {
    ...
    androidTestImplementation "android.arch.persistence.room:testing:1.0.0"
    }

    View Slide

  46. Testing Migrations - Create test rule
    @Rule
    public MigrationTestHelper mMigrationTestHelper =
    new MigrationTestHelper (InstrumentationRegistry.getInstrumentation (),
    AppDatabase.class.getCanonicalName (),
    new FrameworkSQLiteOpenHelperFactory ());

    View Slide

  47. Testing Migrations - Migrate and validate
    @Test
    public void migrationFrom1To2_containsCorrectData () throws IOException {
    SupportSQLiteDatabase db = mMigrationTestHelper.createDatabase (DB_NAME, 1);
    db.execSQL ("INSERT INTO attendee VALUES(" + ATTENDEE.getId () + ",'"
    + ATTENDEE.getName () + "','" + ATTENDEE.getEmail () + "','"
    + ATTENDEE.getCategory () + "')");
    db.close ();
    mMigrationTestHelper.runMigrationsAndValidate (DB_NAME, 2, validateDroppedTables
    ,
    MIGRATION_1_2);
    ...
    }

    View Slide

  48. Testing Migrations - Migrate and validate
    @Test
    public void migrationFrom1To2_containsCorrectData () throws IOException {
    ...
    Attendee attendee = getMigratedRoomDatabase ().attendeeDao ().
    getAttendedByEmail (ATTENDEE.getEmail ());
    assertEquals (ATTENDEE.getId (), attendee.getId ());
    assertEquals (ATTENDEE.getName (), attendee.getName ());
    assertEquals (attendee.getPhoneNumber (), null);
    }

    View Slide

  49. Show Me The Code
    I have provided a repo that demonstrates all that have been said.
    It has two branches: room-kotlin and room-java
    Check it out:
    https://github.com/larikraun/RoomDevfestSW17

    View Slide

  50. References
    https://developer.android.com/training/data-storage/room/index.html
    https://codelabs.developers.google.com/codelabs/android-persistence/index.ht
    ml#0
    https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1

    View Slide

  51. Omolara Adejuwon
    (@_larikraun)
    Android, iOS @ LawPavilion
    THANK YOU
    #DevFest17 #DevFestSW

    View Slide